tictactoe: initial import
[?]
Dec 22, 2020, 3:02 PM
G5KXCVILVVZVQAAVLZQJ2OKJ6LHNICSSKLDNCA3LXVUAMPWJDYRQCDependencies
- [2]
AUM6AXH7It is enough to check only half of the string because of symmetry - [3]
2P4Y7M5Rpalindrome: use slices in the concurrent version too. The single thread version is so fast now, that using channels makes this one slower - [4]
O64GEJAPpalindrome: do not report one char long palindromes - [5]
L5UJYVXWpalindrome: Use string slice instead of concatenating, this makes the code WAY faster - [6]
MCHVA5DYPalindrome initial import - [7]
VUXCDW3Lpalindrome: move to palindrome package - [8]
ZQFONDI4Add simple version
Change contents
- file addition: tictactoe[4.11]
- file addition: tictactoe.go[0.12]
package tictactoeimport ("fmt""os")const debug = true// WinSize sets how many we need next to each otherconst WinSize = 3// Runes for record typesconst (Empty rune = '_'Pl1 rune = 'x'Pl2 rune = 'o')func debugPrinter(format string, a ...interface{}) {if debug {fmt.Fprintf(os.Stderr, format+"\n", a...)}} - file addition: minimax_test.go[0.12]
package tictactoeimport ("math""testing""github.com/stretchr/testify/assert")func TestMinimax(t *testing.T) {var testCases = []struct {scores []intexpected int}{{[]int{3, 5}, 5},{[]int{3, 5, 2, 9}, 3},{[]int{3, 5, 2, 9, 12, 5, 23, 23}, 12},{[]int{3, 5, 2, 9, 1, 2, 3}, 3},}for _, tc := range testCases {height := math.Log2(float64(len(tc.scores)))t.Run("Minimax", func(t *testing.T) {//debugPrinter("len(scores): %d, height: %v int(height): %d", len(tc.scores), height, int(height))assert.Equal(t, tc.expected, minimax(0, 0, true, tc.scores, int(height)))})}} - file addition: minimax.go[0.12]
package tictactoe// minimax is for retrieving the optimal value for maximizer// depth is the current depth in the game tree// nodeIndex is the current index of node in scores// scores stores the Game Tree// isMax is true if the current move is a maximizer// height is the max height of the Game Treefunc minimax(depth, nodeIndex int, isMax bool, scores []int, height int) int {// terminate if end of the tree reachedif depth == height {return scores[nodeIndex]}if isMax {left := minimax(depth+1, nodeIndex*2, false, scores, height)right := minimax(depth+1, nodeIndex*2+1, false, scores, height)return max(left, right)} else {left := minimax(depth+1, nodeIndex*2, true, scores, height)right := minimax(depth+1, nodeIndex*2+1, true, scores, height)return min(left, right)}}func max(a, b int) int {if a > b {return a}return b}func min(a, b int) int {if a < b {return a}return b} - file addition: go.mod[0.12]
module tictactoego 1.15require github.com/stretchr/testify v1.6.1 - file addition: evaluate_test.go[0.12]
package tictactoeimport ("testing""github.com/stretchr/testify/assert")func TestEvaluate(t *testing.T) {var testCases = []struct {description stringboard [][]runeexpected int}{{"Nothing", [][]rune{{Pl1, Pl1, Pl2}, {Empty, Empty, Empty}, {Empty, Empty, Empty}}, 0},{"Pl1 row", [][]rune{{Pl1, Pl1, Pl1}, {Empty, Empty, Empty}, {Empty, Empty, Empty}}, 10},{"Pl2 col", [][]rune{{Pl1, Pl1, Pl1}, {Pl2, Empty, Empty}, {Pl2, Empty, Empty}}, -10},{"Pl1 diag", [][]rune{{Pl2, Pl1, Pl1}, {Empty, Pl1, Empty}, {Pl1, Empty, Empty}}, 10},}for _, tc := range testCases {t.Run("Evaluate"+tc.description, func(t *testing.T) {assert.Equal(t, tc.expected, evaluate(tc.board))})}} - file addition: evaluate.go[0.12]
package tictactoefunc evaluate(board [][]rune) int {for row := range board {for col := range board[row] {if checkRow(board, row, col, WinSize, Pl1) == WinSize {return +10} else if checkRow(board, row, col, WinSize, Pl2) == WinSize {return -10}if checkCol(board, row, col, WinSize, Pl1) == WinSize {return +10} else if checkCol(board, row, col, WinSize, Pl2) == WinSize {return -10}if checkDiag(board, row, col, WinSize, Pl1) == WinSize {return +10} else if checkDiag(board, row, col, WinSize, Pl2) == WinSize {return -10}}}return 0}func checkRow(board [][]rune, row, col, winsize int, player rune) (goodness int) {for i := 0; i < winsize; i++ {if row+i < len(board[col]) {if board[row+i][col] == player {goodness++} else {return goodness}}}return goodness}func checkCol(board [][]rune, row, col, winsize int, player rune) (goodness int) {for j := 0; j < winsize; j++ {if col+j < len(board[row]) {if board[row][col+j] == player {goodness++} else {return goodness}}}return goodness}func checkDiag(board [][]rune, row, col, winsize int, player rune) (goodness int) {for k := 0; k < winsize; k++ {if row+k < len(board[col]) && col+k < len(board[row]) {if board[row+k][col+k] == player {goodness++} else {return goodness}}}return goodness} - edit in snippets/palindrome/palindrome.go at line 6
import ("sync") - replacement in snippets/palindrome/palindrome.go at line 40
jobs := 0var wg sync.WaitGroup - replacement in snippets/palindrome/palindrome.go at line 42[4.590]→[2.0:37](∅→∅),[2.37]→[4.615:628](∅→∅),[4.615]→[4.615:628](∅→∅),[4.628]→[3.21:54](∅→∅),[3.54]→[4.662:676](∅→∅),[4.662]→[4.662:676](∅→∅)
for k := 0; k < len(str)/2+1; k++ {if k > 0 {go palWorker(str, k, results)jobs++}for k := 1; k < len(str)/2+1; k++ {wg.Add(1)go func(char string, num int) {defer wg.Done()results <- palWorker(char, num)}(str, k) - replacement in snippets/palindrome/palindrome.go at line 49
for {select {case candidate := <-results:jobs--if len(candidate) > len(longest) {longest = candidate}if jobs == 0 {close(results)return longest}go func() {wg.Wait()close(results)}()for candidate := range results {if len(candidate) > len(longest) {longest = candidate - edit in snippets/palindrome/palindrome.go at line 58
return longest - replacement in snippets/palindrome/palindrome.go at line 61
func palWorker(str string, k int, result chan<- string) {func palWorker(str string, k int) string { - replacement in snippets/palindrome/palindrome.go at line 70
result <- longestreturn longest