box-o-sand/conway/game_state.go
Dan Buch 3e013ce1a5 Too much crap for one commit
Breaking out console runner into its own file, adding an empty web
runner, and starting to test that games and game states are able to
represent themselves as images.
2012-12-16 17:59:54 -05:00

181 lines
3.1 KiB
Go

package conway
import (
"crypto/sha1"
"errors"
"fmt"
"image"
"math"
"math/rand"
"strings"
)
const (
DEAD_CELL = "■"
LIVE_CELL = " "
)
type GameStateCell struct {
Value int
X int
Y int
}
type GameStateRow struct {
Y int
Cells []*GameStateCell
}
type GameState struct {
Rows []*GameStateRow
}
func NewGameState(height, width int) *GameState {
state := &GameState{}
for i := 0; i < height; i++ {
row := &GameStateRow{Y: i}
for j := 0; j < width; j++ {
row.Cells = append(row.Cells, &GameStateCell{
Value: 0,
X: j,
Y: i,
})
}
state.Rows = append(state.Rows, row)
}
return state
}
func (state *GameState) Height() int {
return len(state.Rows)
}
func (state *GameState) Width() int {
if len(state.Rows) < 1 {
return -1
}
return len(state.Rows[0].Cells)
}
func (state *GameState) GetRow(y int) *GameStateRow {
lenRows := len(state.Rows)
if y < 0 {
return state.GetRow(lenRows + y)
}
if y+1 > lenRows {
return state.GetRow(y % lenRows)
}
return state.Rows[y]
}
func (state *GameState) Set(x, y, value int) {
cell := state.Get(x, y)
cell.Value = value
}
func (state *GameState) Get(x, y int) *GameStateCell {
row := state.GetRow(y)
lenCells := len(row.Cells)
if x < 0 {
return state.Get(lenCells+x, row.Y)
}
if x+1 > lenCells {
return state.Get(x%lenCells, row.Y)
}
return row.Cells[x]
}
func (state *GameState) Enliven(x, y int) {
state.Set(x, y, 1)
}
func (state *GameState) Deaden(x, y int) {
state.Set(x, y, 0)
}
func (state *GameState) Import(other *GameState) {
for y, row := range other.Rows {
for x, cell := range row.Cells {
state.Set(x, y, cell.Value)
}
}
}
func (state *GameState) Cells() (<-chan *GameStateCell, error) {
height := state.Height()
width := state.Width()
if height < 0 || width < 0 {
return nil, errors.New("state has invalid dimensions!")
}
cells := make(chan *GameStateCell)
go func(state *GameState, c chan *GameStateCell) {
defer close(c)
height := state.Height()
width := state.Width()
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
c <- state.Get(x, y)
}
}
}(state, cells)
return (<-chan *GameStateCell)(cells), nil
}
func (state *GameState) Mutate() {
randX := rand.Intn(state.Width())
randY := rand.Intn(state.Height())
cell := state.Get(randX, randY)
state.Set(randX, randY, int(math.Abs(float64(cell.Value-1))))
}
func (state *GameState) Checksum() (string, error) {
ck := sha1.New()
cells, err := state.Cells()
if err != nil {
return "", err
}
for cell := range cells {
fmt.Fprintf(ck, "%v", cell.Value)
}
return fmt.Sprintf("%x", ck.Sum(nil)), nil
}
func (state *GameState) String() string {
var rows []string
height := state.Height()
width := state.Width()
for y := 0; y < height; y++ {
var cells []string
for x := 0; x < width; x++ {
stringVal := DEAD_CELL
cell := state.Get(x, y)
if cell.Value == 1 {
stringVal = LIVE_CELL
}
cells = append(cells, stringVal)
}
rows = append(rows, strings.Join(cells, ""))
}
return strings.Join(rows, "\n")
}
func (state *GameState) Image(xMult, yMult int) (*image.Gray16, error) {
return nil, errors.New("Not implemented!")
}