diff --git a/README.md b/README.md index 1339fca..8000f08 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,10 @@ To start a game, run the following command: ## Roadmap - ~~Finish main Menu~~ - Improve board ui - - Show Last move + - ~~Show Last move~~ - ~~Show points better~~ - - Add helpers for liberties, hints and Atari + - ~~Add helpers for liberties, hints and Atari~~ + - Make it look pretty - Add endgame behaviour - Allow human player to start as white - Add Handcap diff --git a/component/game.go b/component/game.go index ad2195a..b3fd21c 100644 --- a/component/game.go +++ b/component/game.go @@ -18,6 +18,11 @@ type BoardState struct { Stones []Oponent Points [2]float32 PlayerTurn Turn + JustPassed bool + LastMove *Position + HLiberties []Position + HGroup []Position + HEnemies []Position } var Board = donburi.NewComponentType[BoardState]() diff --git a/system/render.go b/system/render.go index a92f5bc..7e6f4f4 100644 --- a/system/render.go +++ b/system/render.go @@ -77,10 +77,10 @@ func (r *BoardRender) Draw(ecs *ecs.ECS, screen *ebiten.Image) { boardSize := float32(math.Min(float64(r.bounds.Dx()), float64(r.bounds.Dy()))) cellSize := boardSize / float32(r.configuration.BoardSize+1) clrLines := util.DARK_GREY - clrOutine := color.Black + clrOutline := color.Black if r.configuration.DarkMode { clrLines = util.GREY - clrOutine = color.White + clrOutline = color.White } // Draw Grid Lines @@ -216,7 +216,7 @@ func (r *BoardRender) Draw(ecs *ecs.ECS, screen *ebiten.Image) { vector.DrawFilledCircle(screen, x*cellSize, y*cellSize, cellSize/2, color.White, true) } if pos != component.EMPTY { - vector.StrokeCircle(screen, x*cellSize, y*cellSize, cellSize/2, 2, clrOutine, true) + vector.StrokeCircle(screen, x*cellSize, y*cellSize, cellSize/2, 2, clrOutline, true) } } @@ -225,4 +225,34 @@ func (r *BoardRender) Draw(ecs *ecs.ECS, screen *ebiten.Image) { text.Draw(screen, fmt.Sprint((r.configuration.BoardSize+1)-i), mplusNormalFont, int(boardSize-cellSize*2/3), int(float32(i)*cellSize)+fontSize/4, clrLines) text.Draw(screen, fmt.Sprintf("%c", ALPHABET[i-1]), mplusNormalFont, int(float32(i)*cellSize)-fontSize/4, int(boardSize-cellSize/2), clrLines) } + + // Draw Last Move + if r.board.LastMove != nil { + cell := r.board.Stones[r.board.LastMove.Y*r.configuration.BoardSize+r.board.LastMove.X] + clr := color.Black + if cell == component.BLACK { + clr = color.White + } + vector.StrokeCircle(screen, float32(r.board.LastMove.X+1)*cellSize, float32(r.board.LastMove.Y+1)*cellSize, cellSize/4, 2, clr, true) + } + + // Draw Helpers + for _, lib := range r.board.HLiberties { + clr := clrLines + if len(r.board.HLiberties) == 1 { + clr = util.RED + } + vector.DrawFilledCircle(screen, float32(lib.X+1)*cellSize, float32(lib.Y+1)*cellSize, cellSize/10, clr, true) + } + for _, pc := range r.board.HGroup { + cell := r.board.Stones[pc.Y*r.configuration.BoardSize+pc.X] + clr := color.Black + if cell == component.BLACK { + clr = color.White + } + vector.DrawFilledCircle(screen, float32(pc.X+1)*cellSize, float32(pc.Y+1)*cellSize, cellSize/10, clr, true) + } + for _, enn := range r.board.HEnemies { + vector.DrawFilledCircle(screen, float32(enn.X+1)*cellSize, float32(enn.Y+1)*cellSize, cellSize/10, util.RED, true) + } } diff --git a/system/rules.go b/system/rules.go index 15f631d..0f370fc 100644 --- a/system/rules.go +++ b/system/rules.go @@ -1,8 +1,6 @@ package system import ( - "fmt" - "github.com/hajimehoshi/ebiten/v2" "github.com/joelschutz/goingo/component" "github.com/joelschutz/goingo/event" @@ -36,6 +34,10 @@ func (rs *RuleSystem) HandleInput(w donburi.World, e *event.Interaction) { Y: -1}, Player: rs.board.PlayerTurn, }) + rs.board.HGroup = nil + rs.board.HLiberties = nil + rs.board.HEnemies = nil + rs.board.JustPassed = true rs.board.PlayerTurn = !rs.board.PlayerTurn } else { cell := &rs.board.Stones[e.Position.X+e.Position.Y*int(rs.configuration.BoardSize)] @@ -47,9 +49,9 @@ func (rs *RuleSystem) HandleInput(w donburi.World, e *event.Interaction) { *cell = component.WHITE } // Check for capture - _, _, aEnemies := rs.getArmy(e.Position, nil, nil) + _, _, aEnemies := rs.getArmy(e.Position, nil, nil, nil) for _, p := range aEnemies { - eLib, eConn, _ := rs.getArmy(p, nil, nil) + eLib, eConn, _ := rs.getArmy(p, nil, nil, nil) if len(eLib) == 0 { rs.captureArmy(eConn) event.PointsEvent.Publish(w, &event.Points{ @@ -60,19 +62,25 @@ func (rs *RuleSystem) HandleInput(w donburi.World, e *event.Interaction) { } } // Check for suicide - aLib, _, aEnemies := rs.getArmy(e.Position, nil, nil) + aLib, _, aEnemies := rs.getArmy(e.Position, nil, nil, nil) if len(aLib) != 0 { // End turn event.MoveEvent.Publish(w, &event.Move{ Position: e.Position, Player: rs.board.PlayerTurn, }) + + rs.board.LastMove = &e.Position + rs.board.HGroup = nil + rs.board.HLiberties = nil + rs.board.HEnemies = nil + rs.board.JustPassed = false rs.board.PlayerTurn = !rs.board.PlayerTurn } else { *cell = component.EMPTY } } else { - fmt.Println(rs.getArmy(e.Position, nil, nil)) + rs.board.HLiberties, rs.board.HGroup, rs.board.HEnemies = rs.getArmy(e.Position, nil, nil, nil) } } @@ -84,7 +92,7 @@ func (rs *RuleSystem) captureArmy(army []component.Position) { } } -func (rs *RuleSystem) getArmy(pos component.Position, liberties, connections []component.Position) (lib, conn, enemies []component.Position) { +func (rs *RuleSystem) getArmy(pos component.Position, liberties, connections, enemies []component.Position) (lib, conn, enn []component.Position) { targetColor := rs.cellState(pos) if liberties == nil { @@ -94,8 +102,9 @@ func (rs *RuleSystem) getArmy(pos component.Position, liberties, connections []c connections = make([]component.Position, 0) } connections = append(connections, pos) - enemies = make([]component.Position, 0) - + if enemies == nil { + enemies = make([]component.Position, 0) + } if targetColor > component.EMPTY { // Cell names // | |v1| | @@ -108,7 +117,7 @@ func (rs *RuleSystem) getArmy(pos component.Position, liberties, connections []c liberties = append(liberties, p1) } } else if v1 == targetColor && !rs.inArray(p1, connections) { - liberties, connections, _ = rs.getArmy(p1, liberties, connections) + liberties, connections, enemies = rs.getArmy(p1, liberties, connections, enemies) } else if v1 == rs.oponentStone(targetColor) { enemies = append(enemies, p1) } @@ -119,7 +128,7 @@ func (rs *RuleSystem) getArmy(pos component.Position, liberties, connections []c liberties = append(liberties, p2) } } else if v2 == targetColor && !rs.inArray(p2, connections) { - liberties, connections, _ = rs.getArmy(p2, liberties, connections) + liberties, connections, enemies = rs.getArmy(p2, liberties, connections, enemies) } else if v2 == rs.oponentStone(targetColor) { enemies = append(enemies, p2) } @@ -130,7 +139,7 @@ func (rs *RuleSystem) getArmy(pos component.Position, liberties, connections []c liberties = append(liberties, p3) } } else if v3 == targetColor && !rs.inArray(p3, connections) { - liberties, connections, _ = rs.getArmy(p3, liberties, connections) + liberties, connections, enemies = rs.getArmy(p3, liberties, connections, enemies) } else if v3 == rs.oponentStone(targetColor) { enemies = append(enemies, p3) } @@ -141,7 +150,7 @@ func (rs *RuleSystem) getArmy(pos component.Position, liberties, connections []c liberties = append(liberties, p4) } } else if v4 == targetColor && !rs.inArray(p4, connections) { - liberties, connections, _ = rs.getArmy(p4, liberties, connections) + liberties, connections, enemies = rs.getArmy(p4, liberties, connections, enemies) } else if v4 == rs.oponentStone(targetColor) { enemies = append(enemies, p4) } diff --git a/util/color.go b/util/color.go index 24dbb21..1d639fc 100644 --- a/util/color.go +++ b/util/color.go @@ -7,4 +7,5 @@ var ( DARK_GREY = color.RGBA{64, 64, 64, 255} TRANSPARENT_BLACK = color.RGBA{0, 0, 0, 128} TRANSPARENT_WHITE = color.RGBA{128, 128, 128, 128} + RED = color.RGBA{146, 17, 17, 255} )