Skip to content

Instantly share code, notes, and snippets.

@yurapyon
Created July 9, 2016 22:46
Show Gist options
  • Save yurapyon/53a5a576d2211c186c9a682572f6a751 to your computer and use it in GitHub Desktop.
Save yurapyon/53a5a576d2211c186c9a682572f6a751 to your computer and use it in GitHub Desktop.
tic tac toe with computer player
local string = require"string"
local fmt = string.format
-- ==&== BOARD ==&==
local function new_board()
return {{" "," "," "},
{" "," "," "},
{" "," "," "}}
end
local function print_board(board)
local function print_row(row)
for col = 1,2 do
io.write(fmt(" %s |",board[row][col]))
end
io.write(fmt(" %s \n", board[row][3]))
end
for row = 1, 2 do
print_row(row)
io.write("___|___|___\n")
end
print_row(3)
io.write(" | | \n")
end
-- ==&== CHECK ==&==
--NOTE: for check functions:
--returns nil for no matches or match of " "
-- players letter for match of player letters
local function check_col(board, col)
for row = 1, 2 do
if board[row][col] ~= board[row+1][col] then
return nil
end
end
if board [1][col] ~= " " then
return board[1][col]
end
return nil
end
local function check_row(board, row)
for col = 1, 2 do
if board[row][col] ~= board[row][col+1] then
return nil
end
end
if board [row][1] ~= " " then
return board[row][1]
end
return nil
end
-- NOTE:
-- up diagonal is _ _ X
-- _ X _
-- X _ _
-- not up is the other diagonal
local function check_diag(board, check_up)
if check_up then
if board[1][3] == board[2][2]
and board[2][2] == board[3][1] then
if board[2][2] ~= " " then
return board[2][2]
end
end
else
if board[1][1] == board[2][2]
and board[2][2] == board[3][3] then
if board[2][2] ~= " " then
return board[2][2]
end
end
end
return nil
end
local function check_board(board)
local ret
for i=1,3 do
ret = ret or check_col(board, i)
ret = ret or check_row(board, i)
end
ret = ret or check_diag(board, true)
ret = ret or check_diag(board, false)
return ret
end
-- ==&== READ ==&==
local function computer_put_coord(board, letter)
while true do
local col, row = math.random(3), math.random(3)
if board[col][row] == " " then
board[col][row] = letter
return col, row
end
end
end
local function read_for_and_put_coord(board, letter)
while true do
local input = io.read()
if input ~= "" then
--just pressing enter does not give error but io.read loop continues
--the way lua string.find works is it finds the first instance of the format string
-- and can return the "captured" digits (denoted by parens)
-- with the format i am using below,
-- an input of " 1 2" will return "1" and "2"
-- an input of " 1 2 3" will return "1" and "2" even though "2 3" fits the format
-- an input of "1.5 2 " will return "5" and "2"
-- as this is the first found pattern that fits the format
-- lua does not read "1.5" as a digit but rather a string.
-- a decimal would need to be found with the format: "(%d)%.(%d)"
-- which an input of "1.5" will return "1" and "5" (haha)
-- side note: "%." in the format escapes a '.' as '.' is reserved
--side note #2: string.find will return the location of the found string
-- as the first two return values
-- then all following return values are captured strings
-- here, these first two return values are "dummied" to the variable _
-- because we don't need them
local _, _, col, row = string.find(input, "%s-(%d)%s+(%d)")
col = tonumber(col)
row = tonumber(row)
if col and row then
if col < 4 and col > 0 and row < 4 and row >0 then
if board[col][row] == " " then
board[col][row] = letter
break
else
io.write("HEY someones already put a letter there!!!\n")
end
else
io.write(fmt("sadly, you cant put your letter at (%d, %d)\n", x, y))
end
else
io.write("you cant put your letter... there...(o.O\n")
end
io.write("please try again...\n")
end
end
end
local function read_for_letter()
while true do
local _,_,ret = string.find(io.read(), "%s-(.)")
if ret then
return ret
end
end
end
-- returns true for yes
-- or false for no
local function read_for_y_or_n(default_to)
if default_to == nil then default_to = false end
while true do
local _, _, ret = string.find(io.read(), "%s-(.)")
if not ret then
return default_to
elseif ret == "y" or ret == "Y" or ret == "yes" then
return true
elseif ret == "n" or ret == "N" or ret == "no" then
return false
else
io.write(fmt("'%s' is not, exactly, a vaild answer...\ntry again pls!\n", ret))
end
end
end
-- ==&== MAIN ==&==
local computer_player = false
local function main()
io.write" welcome to tic tac toe! \n"
io.write" ~~~~~~~~~~~~~~~~~~~~~~~ \n"
local p1,p2
local choose_letters = true
local first_coord = true
io.write"would you like to play against a computer player? [Y,n]\n"
computer_player = read_for_y_or_n(true)
if computer_player then
--generate fresh seed for the game
math.randomseed(os.time())
end
while true do --loop for repeat games
if choose_letters then
io.write"player 1 choose your letter!\n"
p1 = read_for_letter()
if p1 == "x" then
p2 = "o"
elseif p1 == "X" then
p2 = "O"
elseif p1 == "o" then
p2 = "x"
elseif p1 == "O" then
p2 = "X"
else
while true do
if computer_player then
--generate number between unicode char values A and Z
-- then use string.format to turn into a char
p2 = fmt("%c", math.random(65,90))
if p1 ~= p2 then break end
else
io.write("player 2 choose your letter!\n")
p2 = read_for_letter()
if p2 ~= p1 then
break
else
io.write("ya can't pick the same letter as player 1......\n")
end
end
end
end
end
io.write(fmt("\nplayer 1 is %s's\n%s is %s's\n"
, p1
, computer_player and "computer" or "player 2"
, p2))
io.write("lets play!\n\n")
local board = new_board()
local outcome = nil
local p1s_turn = true
local turns = 0
while not outcome do
-- turn
if first_coord then
io.write("*whisper* please format your coords as: over#[space]down#\n")
first_coord = false
end
if computer_player and not p1s_turn then
--you will only get here for a computer player
local col, row = computer_put_coord(board, p2)
io.write(fmt("computer player has placed thier letter at %d %d\n", col, row))
--note: i would like to put a sleep function here
-- however this is not built into lua and would have to be brought in via FFI
-- its pretty trivial to do this but i don't really want to risk the
-- code not working on your computer (as the sleep function is OS specific)
-- and while its also trivial to check for OS, i feel better being 100% safe haha
else
io.write(fmt("%s pick a coordinate...\n", p1s_turn and "player 1" or "player 2"))
read_for_and_put_coord(board, p1s_turn and p1 or p2)
end
p1s_turn = not p1s_turn
turns = turns + 1
-- print and chek
print_board(board)
outcome = check_board(board)
if turns >= 9 then
break
end
end
io.write("\nfinissshhh...\n\n")
if not outcome then
io.write("dangg it was a draww..\n")
else
if outcome == p1 then
io.write("o gr8 job player 1! real nice going..\n")
else
if computer_player then
io.write("the computer has defeated you (somehow)\n")
else
io.write("o gr8 job player 2! real nice going..\n")
end
end
end
io.write("wanna play again, huh? [y,N]\n")
if read_for_y_or_n(false) then
io.write("greeeeeeat...\n")
io.write("choose new letters? [y,N]\n")
choose_letters = read_for_y_or_n(false)
else
io.write("thanks for playing!\n")
break
end
end
end
main()
@yurapyon
Copy link
Author

yurapyon commented Jul 9, 2016

quick note on lua distributions:
mainstream Lua is interpreted
and does not offer the speed or FFI capabilities of its popular fork, Luajit

Luajit can also be interpreted
but can be jit compiled as well
luajit allows for very easy C interop
(which is typically as easy as copy-pasting a header file)

i use luajit which is not 100% compatible with mainstream Lua versions 5.2 or 5.3
yet it is fully compatible with mainstream Lua 5.1

So, to install on your machine
Linux:
I know for a fact Linux Mint and Arch Linux have Luajit in thier repositories
so installing would just require installing from there

For other operating systems/ to build Luajit on your own:
The source can be downloaded from here:
http://luajit.org/install.html
and there are instructions on doing that.

If you don't have Linux and arent interested in building it:
some binaries for mainstream Lua can be found here
http://luabinaries.sourceforge.net/download.html
please download Lua5.1 as i have tested this code with it also

to run:
download/ copy-paste the code into tic_tac_toe.lua
open a terminal and
run luajit (or lua5.1) from the commandline with that file as the second parameter

ex: "luajit tic_tac_toe.lua"

to end the program prematurely, CTRL+C must be pressed twice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment