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.