Hello I am making tic tac toe and I figured out how to check the horizontal (rows) for a match and it works as it should but now I also want it to check for the vertical and diagonal. Thanks.
local board = {
{"x", "o", "x"},
{"o", "x", "o"},
{"x", "o", "x"}
}
local function checkHorizontal(plrname, other)
for row = 1, #board do
local first = board[row][1]
for column = 2, #board[row] do
if board[row][column] == 0 or board[row][column] ~= first then
break
elseif column == #board[row] then
local winner = board[row][column]
if winner == player.Value or winner == plrname and debounce == false then
--code stuff
end
return winner
end
end
end
end
You can only win when you place a piece down, so you really only need to check 3 things each move rather than the entire board.
That is, check horizontal, diagonal, and vertical only if they contain the most recently placed piece.
Perhaps this simplifies the problem for you and you’ll find it easier to write!
The logic for a vertical check is exactly the same as a horizontal check, except you’re swapping the indexes of the row and column. For example, a win condition for this board:
Okay I understand that. But I don’t really understand how I would implement it in my code. I tried…
for row = 1, #board do
local first = board[row][1]
for column = 2, #board[row] do
if board[column][row] == 0 or board[column][row] ~= first then
break
elseif row == #board[column] then
local winner = board[column][row]
if winner == player.Value or winner == plrname and debounce == false then
--stuff
end
return winner
end
end
end
Here’s a similar vertical function and diagonal functions. You can quickly see that there is a lot of code reduction an optimization to do, but at least it should give you a place to start:
local BOARD_SIZE = 3
local function checkHorizontal(plrname, other)
for y = 1, BOARD_SIZE do
local row = board[y]
local first = row[1]
if first == 0 then
break
end
for x = 2, BOARD_SIZE do
local cell = row[x]
if cell ~= first then
break
elseif x == BOARD_SIZE then
local winner = cell
if winner == player.Value or winner == plrname and debounce == false then
-- code stuff
end
return winner
end
end
end
end
local function checkVertical(plrname, other)
for x = 1, BOARD_SIZE do
local first = board[1][x]
if first == 0 then
break
end
for y = 2, BOARD_SIZE do
local row = board[y]
local cell = row[x]
if cell ~= first then
break
elseif y == BOARD_SIZE then
local winner = cell
if winner == player.Value or winner == plrname and debounce == false then
-- code stuff
end
return winner
end
end
end
end
local function checkBackDiagonal(plrname, other)
-- Top left to bottom right
local first = board[1][1]
if first == 0 then
return
end
for i = 2, BOARD_SIZE do
local row = board[i]
local cell = row[i]
if cell ~= first then
break
elseif i == BOARD_SIZE then
local winner = cell
if winner == player.Value or winner == plrname and debounce == false then
-- code stuff
end
return winner
end
end
end
local function checkForwardDiagonal(plrname, other)
-- Top right to bottom left
local first = board[1][BOARD_SIZE]
if first == 0 then
return
end
for i = 2, BOARD_SIZE do
local row = board[i]
local cell = row[BOARD_SIZE - i + 1]
if cell ~= first then
break
elseif i == BOARD_SIZE then
local winner = cell
if winner == player.Value or winner == plrname and debounce == false then
-- code stuff
end
return winner
end
end
end
Okay thanks. I have it so when you click a field to choose it does not put in “X” or “O” in the table it just adds the player’s name so I can print it out at the end when there is a win.
I can see that it does not work right now how it is so I need to get it to find matching names and not “X” or “O”. How do I do that?
Can’t you just check if the strings on each row match to see if there’s a winner? For the horizontal one i mean. Something like:
for row = 1, #board do
if board[row][1] == board[row][2] and board[row][2] == board[row][3] then
local winner = board[row][1] -- for example, it could be any column. We just need to know if its x or o.
return winner
end
end
and for vertical ones, it could be the same but flipped:
for column= 1, #board do
if board[1][column] == board[2][column] and board[2][column] == board[3][column] then
local winner = board[1][column] -- for example, it could be any column. We just need to know if its x or o.
return winner
end
end
i haven’t tested this, but i think it could be a more simple way of doing it
This is definitely a simpler way of doing it, but it gets very long to do it this way if you want to check the win condition for a board of any size. OP didn’t specify that it had to be a board of any size, however, so if it’s always only 3, then this may be a nice solution.
The code I provided is agnostic of the string value, the only hardcoded value there is 0 for an “unfilled” space. Otherwise, all it does is check that the value is the same as the “first” cell (which is different for each method), which should work fine whether your string is an “x”, an “o”, or someone’s username-- as long as it’s consistent for all 3 winning cells.
local function getWinner()
for i = 1, 3 do
-- Check horizontal
if board[i][1] ~= 0 and board[i][1] == board[i][2] and board[i][2] == board[i][3] then
return board[i][1]
end
-- Check vertical
if board[1][i] ~= 0 and board[1][i] == board[2][i] and board[2][i] == board[3][i] then
return board[1][i]
end
-- Check top left to bottom right
if board[2][2] ~= 0 and board[1][1] == board[2][2] and board[2][2] == board[3][3] then
return board[2][2]
end
-- Check top right to bottom left
if board[2][2] ~= 0 and board[1][3] == board[2][2] and board[2][2] == board[3][1] then
return board[2][2]
end
end
return nil
end
The vertical and diagonal stil seems to not work for me… This is how I have set it up with your version:
local function getWinner(plrname, other)
for i = 1, 3 do
-- Check horizontal
if board[i][1] ~= 0 and board[i][1] == board[i][2] and board[i][2] == board[i][3] then
local winner = board[i][1]
return board[i][1]
end
-- Check vertical
if board[1][i] ~= 0 and board[1][i] == board[2][i] and board[2][i] == board[3][i] then
local winner = board[1][i]
return board[i][1]
end
-- Check top left to bottom right
if board[2][2] ~= 0 and board[1][1] == board[2][2] and board[2][2] == board[3][3] then
local winner = board[1][1]
return board[2][2]
end
-- Check top right to bottom left
if board[2][2] ~= 0 and board[1][3] == board[2][2] and board[2][2] == board[3][1] then
local winner = board[1][3]
return board[2][2]
end
end
return nil
end
I had a mistake in my original return statement for vertical, I was returning the wrong value. I edited my previous post to correct the error.
In your version, you’re saving the winner in a local variable, which isn’t used anywhere. The proper way to use this function is to call it and use the return value, like so:
local winner = getWinner()
if winner then
-- Do winner code here, where winner is the value saved in the cells of the winner
end
Shouldn’t need to pass plrname or other into the getWinner function.
Can you post a more complete sample of your code so we can troubleshoot it? The function I posted works, it’s probably your usage of it that isn’t set up right.
The getWinner() is as default I haven’t changed anything there. Then I have made a function to call everything I want and display the win:
local function afterWin()
local winner = getWinner()
if winner and winner == player.Value or winner == plr.Name and debounce == false then
debounce = true
print("Winner:", winner)
game.Players[plr.Name].PlayerGui.TicTacToe.winValue.Value = 1
game.Players[plr.Name].PlayerGui.TicTacToe.Frame.GameFrame.Visible = false
game.Players[player.Value].PlayerGui.TicTacToe.Frame.GameFrame.Visible = false
game.Players[plr.Name].PlayerGui.TicTacToe.Frame.Status.Text = winner.." won the game!"
game.Players[player.Value].PlayerGui.TicTacToe.Frame.Status.Text = winner.." won the game!"
wait(3)
game.Players[plr.Name].PlayerGui.TicTacToe.Frame.Visible = false
game.Players[player.Value].PlayerGui.TicTacToe.Frame.Visible = false
game.Players[plr.Name].PlayerGui.TicTacToe.Clicked.Value = false
game.Players[player.Value].PlayerGui.TicTacToe.Clicked.Value = false
game.Players[plr.Name].PlayerGui.TicTacToe.winValue.Value = 0
script.Parent.Count.Value = nil
script.Parent.Player.Value = ""
script.Parent.secondPlayer.Value = ""
for i,v in pairs(game.Players[plr.Name].PlayerGui.TicTacToe.Frame.GameFrame:GetChildren()) do
if v:IsA("TextButton") then
v.Text = ""
end
end
for i,v in pairs(game.Players[player.Value].PlayerGui.TicTacToe.Frame.GameFrame:GetChildren()) do
if v:IsA("TextButton") then
v.Text = ""
end
end
local new = game.ServerScriptService.mainTTT:Clone()
new.Parent = script.Parent
new.Disabled = false
script:Destroy()
end
end
Then where it gets called:
one2.MouseButton1Click:Connect(function()
bruh.Value = 2
local fn = 1
local sn = 1
one1.Text = "X"
one2.Text = "X"
board[fn][sn] = plr.Name
afterWin()
end)
I suspect the issue may be here, perhaps your values are getting set incorrectly? I’m just taking a guess at your naming scheme here, but does the variable name one2 mean it’s a button in row 1, column 2? If so, note that your fn and sn (not sure what they stand for, but they’re used as row and column indexes) are both 1 and 1, which doesn’t match your variable name one2. If you have a connection like this for each of your board cells, make sure all the indexes are getting set correctly.
Let me know if you’ve double checked this all to be correct and I’ll look elsewhere.