Add life cmd

This commit is contained in:
dwrz
2026-10-23 01:23:33 +00:00
parent d1b67e36f7
commit e4ac0b29bd
+120
View File
@@ -0,0 +1,120 @@
package main
import (
"context"
"flag"
"fmt"
"log/slog"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
"code.chimeric.al/dwrz/life/pkg/life"
"code.chimeric.al/dwrz/life/pkg/terminal"
)
var (
height = flag.Int("h", 0, "height")
width = flag.Int("w", 0, "width")
tick = flag.Int("t", 100, "ms between turns")
)
func main() {
var log = slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
AddSource: true,
Level: slog.LevelDebug,
}))
if err := run(log); err != nil {
log.Error("failed", slog.Any("error", err))
os.Exit(1)
}
}
func run(log *slog.Logger) error {
// Parse flags.
flag.Parse()
if *height < 0 {
return fmt.Errorf("invalid height: %d", *height)
}
if *width < 0 {
return fmt.Errorf("invalid width: %d", *width)
}
if *tick <= 0 {
return fmt.Errorf("invalid tick: %d", *tick)
}
// Setup the main context.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Setup workspace and log file.
cdir, err := os.UserCacheDir()
if err != nil {
return fmt.Errorf(
"failed to determine user cache directory: %w", err,
)
}
wdir := filepath.Join(cdir, "life")
if err := os.MkdirAll(wdir, os.ModeDir|0700); err != nil {
return fmt.Errorf("failed to create tmp dir: %w", err)
}
f, err := os.Create(wdir + "/log")
if err != nil {
return fmt.Errorf("failed to create log file: %w", err)
}
defer f.Close()
var gameLog = slog.New(slog.NewTextHandler(f, &slog.HandlerOptions{
AddSource: true,
Level: slog.LevelDebug,
}))
// Retrieve terminal info.
t, err := terminal.New(os.Stdin.Fd())
if err != nil {
return fmt.Errorf("failed to get terminal attributes: %w", err)
}
size, err := t.Size()
if err != nil {
return fmt.Errorf("failed to get terminal size: %w", err)
}
if *height == 0 {
rows := int(size.Rows)
height = &rows
}
if *width == 0 {
cols := int(size.Columns)
width = &cols
}
// TODO: refactor; handle sigwinch.
go func() {
var signals = make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
// Block until we receive a signal.
s := <-signals
log.Debug("received signal", slog.Any("signal", s))
cancel()
}()
// Setup the game.
var game = life.New(life.Parameters{
Height: *height,
In: os.Stdin,
Log: gameLog,
Out: os.Stdout,
Terminal: t,
Tick: time.Duration(*tick) * time.Millisecond,
Width: *width,
})
// Run the game.
return game.Run(ctx)
}