Godot Version
Godot 4.3
Question
So, as you all know, I have been working on a Ultimate Tic Tac Toe game... Heres how to play it https://en.wikipedia.org/wiki/Ultimate_tic-tac-toe but basically, whenever the Label states for me to play, sometimes it can land on a already won Column and I can't click anything else and I have to reset the game... Any ideas?
Here is a video to show whats happening
Here is the script:
miniboard.gd (The script with the game)
extends Control # Change this to the appropriate base class, if needed
var current_player = "X" # Keep track of the current player
var cells = [] # Store references to buttons
var small_board_wins = [] # Track wins for small boards
var overall_winner = "" # Track overall winner
var is_initialized = false # Track if the board is initialized
var stats: Dictionary = load_stats() # Load or initialize stats at the start
var active_column = -1 # Initialize with -1 to allow any board on the first move
@onready var winner_label = $WinnerLabel # General winner label
@onready var home_button = $HomeTownButton # Reference to the HomeButton
@onready var rematch_button = $RematchYourselfButton # Reference to the Rematch button
@onready var state_labels = [
$State1, $State2, $State3, $State4, $State5, $State6, $State7, $State8, $State9
]
# Declare the variable for your AudioStreamPlayer
var button5_click_player: AudioStreamPlayer = null # Reference to the AudioStreamPlayer for Button5
# Declare labels for each column
var x_win_labels = []
var o_win_labels = []
var tie_labels = [] # Array to hold tie labels for each column
# Function to save stats
func load_stats() -> Dictionary:
var file_path = "user://Stats.cfg"
if not FileAccess.file_exists(file_path):
print("Stats file does not exist. Initializing with default values.")
return {
"Ultimate_Tic_Tac_Toe_Yourself_Won": 0,
"Ultimate_Tic_Tac_Toe_Yourself_Lost": 0,
"Ultimate_Tic_Tac_Toe_AI_Won": 0,
"Ultimate_Tic_Tac_Toe_AI_Lost": 0,
"Simple_Tic_Tac_Toe_AI_Won": 0,
"Simple_Tic_Tac_Toe_Won": 0,
"Simple_Tic_Tac_Toe_AI_Lost": 0,
"Simple_Tic_Tac_Toe_Lost": 0
}
var file = FileAccess.open(file_path, FileAccess.READ)
if file:
var local_stats = {} # Renamed the local variable
while not file.eof_reached():
var line = file.get_line().strip_edges()
var parts = line.split("=")
if parts.size() == 2:
local_stats[parts[0]] = int(parts[1]) # Convert the value to an int
file.close()
return local_stats # Return the renamed variable
else:
print("Failed to open file for reading.")
return {}
func save_stats(new_stats: Dictionary) -> void:
var file_path = "user://Stats.cfg"
var file = FileAccess.open(file_path, FileAccess.WRITE)
if file:
for key in new_stats.keys():
var line = key + "=" + str(new_stats[key]) # Create the line for each key-value pair
file.store_line(line) # Store the line in the file
file.close()
else:
print("Failed to open file for writing.")
func _ready():
for label in state_labels:
label.visible = false
if is_initialized: # Prevent re-initialization
return
is_initialized = true # Mark as initialized
stats = load_stats() # Load existing stats
# Initialize AudioStreamPlayer reference and set the sound stream
button5_click_player = $Button5ClickPlayer # Make sure this node exists in the scene
button5_click_player.stream = load("res://sounds/click.ogg") # Set the path to your sound file
cells.clear()
small_board_wins = [null, null, null, null, null, null, null, null, null] # Initialize small board wins
winner_label.visible = false
home_button.visible = false # Hide the HomeButton initially
rematch_button.visible = false # Hide the Rematch button initially
# Initialize labels for each column
for i in range(1, 10):
# Use 'get_node_safe' or check if the node exists
var x_label = get_node("XWinLabelColumn" + str(i))
var o_label = get_node("OWinLabelColumn" + str(i))
var tie_label = get_node("ItsATieLabelColumn" + str(i))
if x_label and o_label and tie_label:
x_win_labels.append(x_label)
o_win_labels.append(o_label)
tie_labels.append(tie_label)
x_win_labels[i - 1].visible = false
o_win_labels[i - 1].visible = false
tie_labels[i - 1].visible = false
else:
print("One or more labels not found for column: ", i)
# Initialize cells for columns and connect all buttons
for column_index in range(1, 10): # Columns 1 to 9
var grid_container: GridContainer = get_node("GridContainerColumn" + str(column_index))
for cell_index in range(9): # Cells 1 to 9 in each column
var cell: Button = grid_container.get_child(cell_index)
cells.append(cell)
cell.connect("pressed", Callable(self, "_on_Cell_pressed").bind(column_index - 1, cell_index))
# Function to connect buttons safely
func _connect_button(button: Button, handler: String):
if button and !button.is_connected("pressed", Callable(self, handler)):
button.connect("pressed", Callable(self, handler))
else:
print(button.name + " not found or already connected.")
# Function to play button click sound
func _play_button_click_sound():
print("Playing button click sound") # Debugging statement
button5_click_player.play() # Play the button click sound
# Single handler for all Cells (1-81)
func _on_Cell_pressed(column_index: int, cell_index: int) -> void:
if overall_winner != "" or (active_column != -1 and active_column != column_index):
return # Ignore input if game is won or move is in the wrong column
var cell: Button = get_node("GridContainerColumn" + str(column_index + 1)).get_child(cell_index)
# Check if the cell is already occupied or the column has been won
if cell.text != "" or small_board_wins[column_index] != null:
# Show the state label for the specific column
state_labels[column_index].visible = true
return # Do not allow the move if the cell is already filled or the column is won
cell.text = current_player
if check_winner(column_index):
small_board_wins[column_index] = current_player
update_column_win_label(column_index)
lock_column(column_index)
if check_overall_winner():
display_winner(current_player)
lock_game()
return
if is_column_full(column_index):
tie_labels[column_index].visible = true
lock_column(column_index)
current_player = "O" if current_player == "X" else "X"
active_column = cell_index # Set active column based on the cell selected
# Hide all state labels initially
for label in state_labels:
label.visible = false
# Show the label for the next active column if there is one
if overall_winner == "" and active_column != -1:
# Only show the state label if the next column has not been won
if small_board_wins[active_column] == null:
state_labels[active_column].visible = true
state_labels[column_index].visible = false # Hide label after move in column
func update_stats(is_win: bool, player: String):
print("Updating stats - is_win:", is_win, "player:", player) # Debugging output
# Ensure the necessary keys are present in the stats dictionary
if not stats.has("Ultimate_Tic_Tac_Toe_Yourself_Won"):
stats["Ultimate_Tic_Tac_Toe_Yourself_Won"] = 0
if not stats.has("Ultimate_Tic_Tac_Toe_Yourself_Lost"):
stats["Ultimate_Tic_Tac_Toe_Yourself_Lost"] = 0
# Update stats based on win/loss and player
if player == "X":
if is_win:
stats["Ultimate_Tic_Tac_Toe_Yourself_Won"] += 1
elif player == "O":
if is_win:
stats["Ultimate_Tic_Tac_Toe_Yourself_Lost"] += 1
save_stats(stats)
func display_winner(player: String) -> void:
if player == "X":
update_stats(true, "X") # Player wins
update_stats(false, "O") # AI loses
else:
update_stats(false, "X") # Player loses
update_stats(true, "O") # AI wins
# Other display logic remains the same
for label in x_win_labels:
label.visible = false
for label in o_win_labels:
label.visible = false
# Show the general winner label
winner_label.text = player + " wins!"
winner_label.visible = true
home_button.visible = true
rematch_button.visible = true
# Call reset game state to prepare for the next game
reset_game_state()
func end_game(is_player_win: bool):
if is_player_win: # If the player won
update_stats(true, "X") # Call with is_win true
else: # Player lost
update_stats(false, "X") # Call with is_win false
func reset_game_state() -> void:
current_player = "X" # Reset current player
small_board_wins = [null, null, null, null, null, null, null, null, null] # Reset small board wins
overall_winner = "" # Reset overall winner
func is_column_full(column_index: int) -> bool:
var grid_container: GridContainer = get_node("GridContainerColumn" + str(column_index + 1))
for i in range(9):
var cell: Button = grid_container.get_child(i)
if cell.text == "":
return false # Return false if any cell is empty
return true # Return true if all cells are filled
func update_column_win_label(column_index: int) -> void:
# Show the appropriate win label for the column
if small_board_wins[column_index] == "X":
x_win_labels[column_index].visible = true
elif small_board_wins[column_index] == "O":
o_win_labels[column_index].visible = true
func lock_column(column_index: int) -> void:
var grid_container: GridContainer = get_node("GridContainerColumn" + str(column_index + 1))
for i in range(9):
var cell: Button = grid_container.get_child(i)
cell.disabled = true
func lock_game() -> void:
for column_index in range(9):
lock_column(column_index)
active_column = -1 # Reset active column to allow any board for rematch
func check_winner(column_index: int) -> bool:
var grid_container: GridContainer = get_node("GridContainerColumn" + str(column_index + 1))
# Check horizontal win
for i in range(3):
if grid_container.get_child(i * 3).text == current_player and \
grid_container.get_child(i * 3 + 1).text == current_player and \
grid_container.get_child(i * 3 + 2).text == current_player:
return true
# Check vertical win
for i in range(3):
if grid_container.get_child(i).text == current_player and \
grid_container.get_child(i + 3).text == current_player and \
grid_container.get_child(i + 6).text == current_player:
return true
# Check diagonal win
if grid_container.get_child(0).text == current_player and \
grid_container.get_child(4).text == current_player and \
grid_container.get_child(8).text == current_player:
return true
if grid_container.get_child(2).text == current_player and \
grid_container.get_child(4).text == current_player and \
grid_container.get_child(6).text == current_player:
return true
return false
func check_overall_winner() -> bool:
for i in range(3):
if (small_board_wins[i * 3] == current_player and \
small_board_wins[i * 3 + 1] == current_player and \
small_board_wins[i * 3 + 2] == current_player) or \
(small_board_wins[i] == current_player and \
small_board_wins[i + 3] == current_player and \
small_board_wins[i + 6] == current_player):
return true
if (small_board_wins[0] == current_player and \
small_board_wins[4] == current_player and \
small_board_wins[8] == current_player) or \
(small_board_wins[2] == current_player and \
small_board_wins[4] == current_player and \
small_board_wins[6] == current_player):
return true
return false
func _on_hometown_button_pressed() -> void:
button5_click_player.play() # Play the button click sound
print("HomeButton pressed")
await get_tree().create_timer(0.1).timeout # Add a delay
get_tree().change_scene_to_file("res://UI.tscn") # Update with your actual main menu scene path
func _on_minihomepage_pressed() -> void:
button5_click_player.play() # Play the button click sound
print("MiniHomepage pressed")
await get_tree().create_timer(0.1).timeout # Add a delay
get_tree().change_scene_to_file("res://UI.tscn") # Update with your actual main menu scene path
func _on_rematchyourselfbutton_pressed() -> void:
button5_click_player.play() # Play the button click sound
print("Rematch button pressed")
await get_tree().create_timer(0.1).timeout # Add a delay
# Call functions to reset the game
clear_board()
reset_labels()
reset_game_state()
rematch_button.visible = false # Hide the rematch button after pressing it
home_button.visible = false # hide after pressed
func clear_board() -> void:
for column_index in range(9):
var grid_container: GridContainer = get_node("GridContainerColumn" + str(column_index + 1))
for i in range(9):
var cell: Button = grid_container.get_child(i)
cell.text = ""
cell.disabled = false # Re-enable the cells
func reset_labels() -> void:
for label in x_win_labels:
label.visible = false
for label in o_win_labels:
label.visible = false
for label in tie_labels:
label.visible = false
winner_label.visible = false # Hide the winner label
miniboard SceneTree:
Any help would be very much appreciated!! Thank you so much and have a great night!