From 3471607404cabb1551334fd33bfd10d8e48ddff7 Mon Sep 17 00:00:00 2001
From: Arek <ar@ar76.eu>
Date: Tue, 11 Feb 2025 22:25:57 +0100
Subject: [PATCH] =?UTF-8?q?Optymalizacja=20wy=C5=9Bwietlania?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 cmd/gol/gol.go | 70 +++++++++++++++++++++++++++++++++++---------------
 gol/types.go   | 27 +++++++++++++++----
 term/term.go   | 23 +++++++++--------
 3 files changed, 84 insertions(+), 36 deletions(-)

diff --git a/cmd/gol/gol.go b/cmd/gol/gol.go
index 769d4b3..f176358 100644
--- a/cmd/gol/gol.go
+++ b/cmd/gol/gol.go
@@ -1,18 +1,43 @@
 package main
 
 import (
+	"flag"
 	"gol/gol"
 	"gol/term"
+	"log"
 	"os"
 	"os/signal"
+	"runtime/pprof"
 	"syscall"
 	"time"
 )
 
 func main() {
+	cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file")
+	flag.Parse()
+
+	// Jeśli podano flagę cpuprofile, rozpocznij profilowanie
+	if *cpuprofile != "" {
+		f, err := os.Create(*cpuprofile)
+		if err != nil {
+			log.Fatal("could not create CPU profile: ", err)
+		}
+		defer f.Close()
+		if err := pprof.StartCPUProfile(f); err != nil {
+			log.Fatal("could not start CPU profile: ", err)
+		}
+		defer pprof.StopCPUProfile()
+	}
+	game()
+}
+
+func game() {
+	// arenas - front one is printent, then new state is calculated into back one
+	// next front and back are switched
 	front := gol.New(20, 40)
 	back := gol.New(20, 40)
 
+	// draw initial objects
 	// glider
 	front[0][3] = true
 	front[1][4] = true
@@ -21,35 +46,40 @@ func main() {
 	front[2][4] = true
 
 	// cross
-
 	front[16][21] = true
 	front[17][21] = true
 	front[18][21] = true
 
+	// catch interrupt signals
 	sigs := make(chan os.Signal, 1)
-	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
+	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
 
+	// initialize screen
 	term.StartFullscreen()
 	term.TurnCursor(term.CursorOff)
 
-	loop := func() {
-		ticker := time.NewTicker(250 * time.Millisecond)
-		defer ticker.Stop()
-		for {
-			term.GoHome()
-			front.PrintMe()
-			front.NextGen(back)
-			front, back = back, front
-			select {
-			case <-sigs: // przerwanie
-				return
-			case <-ticker.C: // upłynął czas do kolejnej generacji
-			}
+	defer restoreScreen()
+
+	// setup timer
+	ticker := time.NewTicker(250 * time.Millisecond)
+	defer ticker.Stop()
+
+mainLoop:
+	for {
+		term.GoHome()
+		front.PrintMe()
+		front.NextGen(back)
+		front, back = back, front // switch arenas
+		select {
+		case <-sigs: // interrupt (Ctrl-C etc)
+			break mainLoop
+		case <-ticker.C: // wait for next tick
 		}
 	}
-	defer func() {
-		term.FinishFullscreen()
-		term.TurnCursor(term.CursorOn)
-	}()
-	loop()
+}
+
+// restoreScreen switch back terminal to normal mode.
+func restoreScreen() {
+	term.FinishFullscreen()
+	term.TurnCursor(term.CursorOn)
 }
diff --git a/gol/types.go b/gol/types.go
index 8d30793..fd94667 100644
--- a/gol/types.go
+++ b/gol/types.go
@@ -1,6 +1,10 @@
 package gol
 
-import "fmt"
+import (
+	"bytes"
+	"os"
+	"sync"
+)
 
 type Arena [][]bool
 
@@ -12,18 +16,31 @@ func New(ysize, xsize int) Arena {
 	return arena
 }
 
+// bufPool
+var bufPool = sync.Pool{
+	New: func() any {
+		return new(bytes.Buffer)
+	},
+}
+
 func (a Arena) PrintMe() {
+	b := bufPool.Get().(*bytes.Buffer)
+	b.Reset()
+	spriteOn := ([]byte)("\ue0b6\ue0b4")
+	spriteOff := ([]byte)("\u2022\u2022")
+
 	for i := 0; i < len(a); i++ {
 		for j := 0; j < len(a[0]); j++ {
 			if a[i][j] {
-				fmt.Print("\ue0b6\ue0b4")
+				b.Write(spriteOn)
 			} else {
-				//fmt.Print("\ue0b7\ue0b5")
-				fmt.Print("\u2022\u2022")
+				b.Write(spriteOff)
 			}
 		}
-		fmt.Println()
+		b.WriteRune('\n')
 	}
+	os.Stdout.Write(b.Bytes())
+	bufPool.Put(b)
 }
 
 func (a Arena) NextGen(to Arena) {
diff --git a/term/term.go b/term/term.go
index 186e2db..e83d830 100644
--- a/term/term.go
+++ b/term/term.go
@@ -13,47 +13,48 @@ const (
 
 // TurnCursor turns the cursor on and off according to the parameter.
 func TurnCursor(onOff CursorFlag) {
-	if onOff == CursorOn {
-		fmt.Printf("\033[?25h")
-	} else {
-		fmt.Printf("\033[?25l")
+	switch onOff {
+	case CursorOn:
+		fmt.Print("\033[?25h")
+	case CursorOff:
+		fmt.Print("\033[?25l")
 	}
 }
 
 // ClearScreen clears current screen.
 func ClearScreen() {
-	fmt.Printf("\033[2J")
+	fmt.Print("\033[2J")
 }
 
 // ResetTerminal resets terminal (ESC c)
 func ResetTerminal() {
-	fmt.Printf("\033c")
+	fmt.Print("\033c")
 }
 
 // GoHome moves cursor to the home position (upper left corner)
 func GoHome() {
-	fmt.Printf("\033[H")
+	fmt.Print("\033[H")
 }
 
 // SaveCursor saves curent position of the SaveCursor
 func SaveCursor() {
-	fmt.Printf("\033[s")
+	fmt.Print("\033[s")
 }
 
 // RestoreCursor restore cursor to the position saved previously with SaveCursor().
 func RestoreCursor() {
-	fmt.Printf("\033[u")
+	fmt.Print("\033[u")
 }
 
 // EnableAlternateScreen switches the terminal to the alternative screen.
 // The alternative screen doesn't have the scroll buffer.
 func EnableAlternateScreen() {
-	fmt.Printf("\033[?1049h")
+	fmt.Print("\033[?1049h")
 }
 
 // DiableAlternateScreen switches the terminal to the primary screen.
 func DisableAlternateScreen() {
-	fmt.Printf("\033[?1049l")
+	fmt.Print("\033[?1049l")
 }
 
 // StartFullscreen saves the cursor, enables alternate screen and clears it.