2024-04-23 08:25:40 +00:00
|
|
|
package game
|
2024-04-23 19:12:46 +00:00
|
|
|
|
|
|
|
import (
|
2024-04-24 19:28:48 +00:00
|
|
|
"tictactoe/gameControl"
|
2024-04-23 19:12:46 +00:00
|
|
|
"tictactoe/opponent"
|
|
|
|
|
|
|
|
"fyne.io/fyne/v2"
|
|
|
|
"fyne.io/fyne/v2/container"
|
2024-04-29 08:19:59 +00:00
|
|
|
"fyne.io/fyne/v2/dialog"
|
2024-04-23 19:12:46 +00:00
|
|
|
"fyne.io/fyne/v2/layout"
|
|
|
|
"fyne.io/fyne/v2/widget"
|
|
|
|
)
|
|
|
|
|
|
|
|
var board = [3][3]string{
|
|
|
|
{" ", " ", " "},
|
|
|
|
{" ", " ", " "},
|
|
|
|
{" ", " ", " "},
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
PLAYER_X = "X"
|
|
|
|
PLAYER_O = "O"
|
|
|
|
)
|
|
|
|
|
|
|
|
var gridContainer *fyne.Container
|
|
|
|
|
|
|
|
// creates the button grid for the tictactoe game
|
2024-04-29 08:19:59 +00:00
|
|
|
func MakeButtonGrid(rows, cols int, Window fyne.Window) *fyne.Container {
|
2024-04-23 19:12:46 +00:00
|
|
|
// generates layout and grid container
|
|
|
|
gridLayout := layout.NewGridLayout(cols)
|
|
|
|
gridContainer = container.New(gridLayout)
|
|
|
|
// create the 3x3 Button grid
|
|
|
|
for row := 0; row < rows; row++ {
|
|
|
|
for col := 0; col < cols; col++ {
|
2024-04-29 08:19:59 +00:00
|
|
|
button := CreateButton(row, col, Window)
|
2024-04-23 19:12:46 +00:00
|
|
|
gridContainer.Add(button)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return gridContainer
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the button grid and adds key press handler for each grid-button
|
2024-04-29 08:19:59 +00:00
|
|
|
func CreateButton(row, col int, Window fyne.Window) *widget.Button {
|
2024-04-23 19:12:46 +00:00
|
|
|
button := widget.NewButton(" ", nil)
|
|
|
|
|
|
|
|
button.OnTapped = func() {
|
|
|
|
if button.Text == " " {
|
|
|
|
button.SetText(PLAYER_X)
|
2024-04-29 08:19:59 +00:00
|
|
|
Gameloop(row, col, Window)
|
2024-04-23 19:12:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return button
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset the game by clearing the buttons and the board array
|
|
|
|
func ResetButtonTexts(container *fyne.Container) {
|
|
|
|
for _, child := range container.Objects {
|
|
|
|
if btn, ok := child.(*widget.Button); ok {
|
|
|
|
btn.SetText(" ")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
board = [3][3]string{
|
|
|
|
{" ", " ", " "},
|
|
|
|
{" ", " ", " "},
|
|
|
|
{" ", " ", " "},
|
|
|
|
}
|
|
|
|
EnableAllButtons(gridContainer)
|
|
|
|
}
|
|
|
|
|
|
|
|
// apply AI opponent text to the button
|
|
|
|
func ChangeButtonText(row, col int) {
|
|
|
|
gridContainer.Objects[col+row*3].(*widget.Button).SetText("O")
|
|
|
|
}
|
|
|
|
|
|
|
|
// gameloop function for the game process:
|
|
|
|
// human makes a move, AI counters
|
2024-04-29 08:19:59 +00:00
|
|
|
func Gameloop(row, col int, Window fyne.Window) {
|
|
|
|
const gameOverText string = "Game Over"
|
2024-04-23 19:12:46 +00:00
|
|
|
// starting with the human making a move
|
|
|
|
board[row][col] = PLAYER_X
|
|
|
|
// checking if the game has been won by the human player
|
2024-04-24 19:28:48 +00:00
|
|
|
if gameControl.PlayerHasWon(board, PLAYER_X) {
|
2024-04-29 08:19:59 +00:00
|
|
|
dialog.ShowInformation(gameOverText, "X has won!", Window)
|
2024-04-29 13:32:13 +00:00
|
|
|
DisableAllButtons(gridContainer)
|
|
|
|
return
|
2024-04-23 19:12:46 +00:00
|
|
|
}
|
|
|
|
// checking if the game is a draw
|
2024-04-24 19:28:48 +00:00
|
|
|
if gameControl.BoardisFull(board) {
|
2024-04-29 08:19:59 +00:00
|
|
|
dialog.ShowInformation(gameOverText, "It's a draw!", Window)
|
2024-04-29 13:32:13 +00:00
|
|
|
DisableAllButtons(gridContainer)
|
|
|
|
return
|
2024-04-23 19:12:46 +00:00
|
|
|
}
|
|
|
|
// AI opponent makes a move
|
|
|
|
bestMove := opponent.Minimax(board, 0, true, PLAYER_O)
|
|
|
|
// add the move to the board
|
|
|
|
board[bestMove.Row][bestMove.Col] = PLAYER_O
|
|
|
|
ChangeButtonText(bestMove.Row, bestMove.Col)
|
|
|
|
|
|
|
|
// check if AI opponent has won the game
|
2024-04-24 19:28:48 +00:00
|
|
|
if gameControl.PlayerHasWon(board, PLAYER_O) {
|
2024-04-29 08:19:59 +00:00
|
|
|
dialog.ShowInformation(gameOverText, "O has won!", Window)
|
2024-04-23 19:12:46 +00:00
|
|
|
DisableAllButtons(gridContainer)
|
2024-04-29 13:32:13 +00:00
|
|
|
return
|
2024-04-23 19:12:46 +00:00
|
|
|
}
|
|
|
|
// checking if the game is a draw
|
2024-04-24 19:28:48 +00:00
|
|
|
if gameControl.BoardisFull(board) {
|
2024-04-29 08:19:59 +00:00
|
|
|
dialog.ShowInformation(gameOverText, "It's a draw!", Window)
|
2024-04-25 06:33:37 +00:00
|
|
|
DisableAllButtons(gridContainer)
|
2024-04-29 13:32:13 +00:00
|
|
|
return
|
2024-04-23 19:12:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// function to disable all buttons inside the grid
|
|
|
|
func DisableAllButtons(container *fyne.Container) {
|
|
|
|
for _, child := range container.Objects {
|
|
|
|
if btn, ok := child.(*widget.Button); ok {
|
|
|
|
btn.Disable()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// function to enable all buttons inside the grid
|
|
|
|
func EnableAllButtons(container *fyne.Container) {
|
|
|
|
for _, child := range container.Objects {
|
|
|
|
if btn, ok := child.(*widget.Button); ok {
|
|
|
|
btn.Enable()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|