Guest User

tic-tac-go

a guest
Jun 26th, 2026
27
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.44 KB | None | 0 0
  1. package main
  2.  
  3. import (
  4. "bufio"
  5. "errors"
  6. "fmt"
  7. "os"
  8. "strconv"
  9. "strings"
  10. "unicode"
  11. )
  12.  
  13. const (
  14. boardSize = 3
  15. p1 int = 1
  16. p2 int = 2
  17. cell string = "|%s"
  18. boardSquare = boardSize * boardSize
  19. )
  20.  
  21. var (
  22. turn int = p1
  23. board [boardSquare]int = [boardSquare]int{}
  24. icon map[int]string = map[int]string{
  25. 0: " ",
  26. 1: "X",
  27. 2: "O",
  28. }
  29. roof string = ""
  30. floor string = strings.Repeat("-", (boardSize*2)+1)
  31. minChar string = "a"
  32. maxChar string = string('a' + boardSize)
  33. )
  34.  
  35. func readLine() (string, error) {
  36. scanner := bufio.NewScanner(os.Stdin)
  37. text := ""
  38. if scanner.Scan() {
  39. text = scanner.Text()
  40. }
  41.  
  42. if err := scanner.Err(); err != nil {
  43. return "", err
  44. }
  45.  
  46. text = strings.ReplaceAll(text, "\r", "")
  47. text = strings.ReplaceAll(text, "\n", "")
  48.  
  49. return text, nil
  50. }
  51.  
  52. func nextPlayer() {
  53. if turn == p1 {
  54. turn = p2
  55. return
  56. }
  57. turn = p1
  58. }
  59.  
  60. func gameState() {
  61. fmt.Printf(" %v\n", roof)
  62. for i := 0; i < boardSquare; i++ {
  63. if i%boardSize == 0 {
  64. row := i / boardSize
  65. rowLetter := string('a' + row)
  66. fmt.Print(rowLetter)
  67. }
  68. fmt.Printf(cell, icon[board[i]])
  69. if (i+1)%boardSize == 0 {
  70. fmt.Printf("|\n")
  71. }
  72. }
  73. fmt.Printf(" %v\n", floor)
  74. }
  75.  
  76. func resetBoard() {
  77. for i := 0; i < boardSquare; i++ {
  78. board[i] = 0
  79. }
  80. turn = p1
  81. }
  82.  
  83. func flush() {
  84. fmt.Print("\033[H\033[2J")
  85. }
  86.  
  87. func promptRepeat() bool {
  88. fmt.Println("Would you like to play again? Y/N")
  89. in, err := readLine()
  90. if err != nil {
  91. return false
  92. }
  93. return strings.ToLower(in) == "y"
  94. }
  95.  
  96. func turnStringInt(turnString string) (int, error) {
  97. if len(turnString) != 2 {
  98. return 0, errors.New("turn string must be 2 chars")
  99. }
  100.  
  101. r := turnString[0]
  102. if !unicode.IsLetter(rune(r)) {
  103. return 0, errors.New("first char must be a letter")
  104. }
  105.  
  106. row := rune(r) - 'a'
  107. if row > boardSize || row < 0 {
  108. return 0, fmt.Errorf("row must be between %s and %s", minChar, maxChar)
  109. }
  110.  
  111. c := turnString[1]
  112. if !unicode.IsDigit(rune(c)) {
  113. return 0, errors.New("second char must be a digit")
  114. }
  115.  
  116. col, err := strconv.Atoi(string(c))
  117. if err != nil {
  118. return 0, errors.New("second char must be a digit")
  119. }
  120.  
  121. if col > boardSize+1 || col < 0 {
  122. return 0, fmt.Errorf("column must be between 1 and %v", boardSize+1)
  123. }
  124.  
  125. return (int(row) * boardSize) + (col - 1), nil
  126. }
  127.  
  128. func takeTurn() error {
  129. fmt.Printf("Player %v enter your turn:\n", turn)
  130. turnString, err := readLine()
  131. if err != nil {
  132. return err
  133. }
  134.  
  135. i, err := turnStringInt(turnString)
  136. if err != nil {
  137. return err
  138. }
  139.  
  140. if board[i] != 0 {
  141. return fmt.Errorf("space at %v is occupied", turnString)
  142. }
  143.  
  144. board[i] = turn
  145.  
  146. return nil
  147. }
  148.  
  149. func initRoofString() {
  150. for i := 1; i <= boardSize; i++ {
  151. roof = fmt.Sprintf("%v %v", roof, i)
  152. }
  153. roof = fmt.Sprintf(
  154. "%v\n %v",
  155. roof,
  156. fmt.Sprintf(
  157. "%v",
  158. strings.Repeat("_", (boardSize*2)+1),
  159. ),
  160. )
  161. }
  162.  
  163. func checkWinner() bool {
  164. for i := 0; i < boardSize; i++ {
  165. rowMatch := true
  166. colMatch := true
  167.  
  168. rowPivot := board[i*boardSize]
  169. colPivot := board[i]
  170.  
  171. if rowPivot == 0 {
  172. rowMatch = false
  173. }
  174.  
  175. if colPivot == 0 {
  176. colMatch = false
  177. }
  178.  
  179. for j := 1; j < boardSize; j++ {
  180. if rowMatch && board[i*boardSize+j] != rowPivot {
  181. rowMatch = false
  182. }
  183. if colMatch && board[j*boardSize+i] != colPivot {
  184. colMatch = false
  185. }
  186.  
  187. if !rowMatch && !colMatch {
  188. break
  189. }
  190. }
  191.  
  192. if rowMatch || colMatch {
  193. return true
  194. }
  195. }
  196.  
  197. lDiagPivot := board[0]
  198. if lDiagPivot != 0 {
  199. lDiagMatch := true
  200. for i := 1; i < boardSize; i++ {
  201. if board[i*boardSize+i] != lDiagPivot {
  202. lDiagMatch = false
  203. break
  204. }
  205. }
  206. if lDiagMatch {
  207. return true
  208. }
  209. }
  210.  
  211. rDiagPivot := board[boardSize-1]
  212. if rDiagPivot != 0 {
  213. rDiagMatch := true
  214. for i := 1; i < boardSize; i++ {
  215. if board[i*boardSize+(boardSize-1-i)] != rDiagPivot {
  216. rDiagMatch = false
  217. break
  218. }
  219. }
  220. if rDiagMatch {
  221. return true
  222. }
  223. }
  224. return false
  225. }
  226.  
  227. func main() {
  228. initRoofString()
  229. play := true
  230. for play {
  231. fmt.Println("Starting a new game:")
  232. resetBoard()
  233. for {
  234. gameState()
  235. for err := takeTurn(); err != nil; err = takeTurn() {
  236. fmt.Printf("Error: %v\n", err)
  237. }
  238. flush()
  239. gameState()
  240. if checkWinner() {
  241. fmt.Printf("Player %v has won!\n", turn)
  242. break
  243. }
  244. nextPlayer()
  245. flush()
  246. }
  247. play = promptRepeat()
  248. flush()
  249. }
  250. }
Advertisement
Add Comment
Please, Sign In to add comment