diff --git a/go/build.sh b/go/build.sh index c550503c..921dcf6a 100755 --- a/go/build.sh +++ b/go/build.sh @@ -7,7 +7,7 @@ DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) source "$DIR/../common.sh" # Define project version here, normally would use git tags for this... -PROJECT_VERSION="1.4.0" +PROJECT_VERSION="1.5.0" USAGE="Usage: $(basename "$0") [OPTIONS] diff --git a/go/othello/board.go b/go/othello/board.go index a1ec3f99..07f4c080 100644 --- a/go/othello/board.go +++ b/go/othello/board.go @@ -10,9 +10,10 @@ package othello import ( "errors" "fmt" + "sort" + mapset "github.com/deckarep/golang-set/v2" "github.com/logrusorgru/aurora/v4" - "sort" ) // Board Handles game board state and logic. @@ -98,11 +99,11 @@ func (b *Board) PlaceDisk(playerMove *Move) { } b.setSquare(&start, playerMove.Disk) b.emptySquares.Remove(start) - for _, step := range playerMove.Directions { - pos := start.Add(step) - for b.getSquare(&pos) == playerMove.Disk.Opponent() { + for _, direction := range playerMove.Directions { + pos := start.Add(direction.Step) + for i := 0; i < direction.Count; i++ { b.setSquare(&pos, playerMove.Disk) - pos = pos.Add(step) + pos = pos.Add(direction.Step) } } } @@ -113,7 +114,7 @@ func (b *Board) PossibleMoves(disk Disk) []Move { opposingDisk := disk.Opponent() for square := range b.emptySquares.Iter() { var value int - var directions []Step + var directions []StepCount for _, step := range b.directions { pos := square.Add(step) // Next square in this direction needs to be the opposing disk @@ -128,7 +129,7 @@ func (b *Board) PossibleMoves(disk Disk) []Move { // Valid move only if a line of opposing disks ends in own disk if b.getSquare(&pos) == disk { value += numSteps - directions = append(directions, step) + directions = append(directions, StepCount{step, numSteps}) } } if value > 0 { @@ -156,7 +157,7 @@ func (b *Board) printPossibleMoves(moves []Move) { } // Add possible moves to board for _, possibleMove := range moves { - index := possibleMove.Square.Y*b.size + possibleMove.Square.X + index := b.squareIndex(&possibleMove.Square) formattedBoard[index] = aurora.Yellow(fmt.Sprintf("%d", possibleMove.Value)).String() fmt.Printf(" %s\n", possibleMove) } @@ -219,13 +220,19 @@ func (b *Board) get(x, y int) (Disk, error) { // Returns the state of the board (empty, white, black) at the given square. func (b *Board) getSquare(square *Square) Disk { if b.checkCoordinates(square.X, square.Y) { - return b.board[square.Y*b.size+square.X] + index := b.squareIndex(square) + return b.board[index] } // Square is out of bounds. // This probably should return a (Disk, error) instead but that makes using this quite cumbersome... return Empty } +// Get all the squares playing this move will change +func (b *Board) squareIndex(square *Square) int { + return square.Y*b.size + square.X +} + // Count and return the number of black and white disks. func (b *Board) playerScores() (int, int) { var black, white int @@ -255,7 +262,8 @@ func (b *Board) setSquare(square *Square, disk Disk) { if !b.checkCoordinates(square.X, square.Y) { panic("Invalid coordinates") } - b.board[square.Y*b.size+square.X] = disk + index := b.squareIndex(square) + b.board[index] = disk } // Format game board to string diff --git a/go/othello/player.go b/go/othello/player.go index ab936d51..b5d4cdbf 100644 --- a/go/othello/player.go +++ b/go/othello/player.go @@ -9,9 +9,10 @@ package othello import ( "fmt" - "github.com/logrusorgru/aurora/v4" "math/rand" "time" + + "github.com/logrusorgru/aurora/v4" ) // Player Defines one player (human or computer). diff --git a/go/othello/utils.go b/go/othello/utils.go index e13d7f86..61ab40bd 100644 --- a/go/othello/utils.go +++ b/go/othello/utils.go @@ -12,8 +12,10 @@ import ( "crypto/sha256" "encoding/hex" "fmt" - "github.com/logrusorgru/aurora/v4" "runtime/debug" + "sort" + + "github.com/logrusorgru/aurora/v4" ) // Set at build time @@ -52,7 +54,16 @@ type Move struct { Square Square Disk Disk Value int - Directions []Step + Directions []StepCount +} + +// Squares implements sort.Interface for a slice of Square objects +type Squares []Square + +// Hold Step and number of steps since Go lacks a tuple / pair construct +type StepCount struct { + Step Step + Count int } // MovesDescending implements sort.Interface with custom sort order. @@ -121,6 +132,10 @@ func (m MovesDescending) Less(i, j int) bool { return false } +func (s Squares) Len() int { return len(s) } +func (s Squares) Less(i, j int) bool { return s[i].IsLessThan(s[j]) } +func (s Squares) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + // BoardChar Returns a single character identifier string for the given disk. func (d Disk) BoardChar() string { switch d { @@ -197,6 +212,20 @@ func (m Move) LogEntry() string { return fmt.Sprintf("%s:%s,%d", m.Disk.BoardChar(), m.Square, m.Value) } +// Get all the squares playing this move will change +func (m Move) AffectedSquares() []Square { + var paths Squares + for _, direction := range m.Directions { + pos := m.Square.Add(direction.Step) + for i := 0; i < int(direction.Count); i++ { + paths = append(paths, pos) + pos = pos.Add(direction.Step) + } + } + sort.Sort(paths) + return paths +} + // Calculate SHA256 hash for the given string. func calculateSHA256(input string) string { data := []byte(input)