Last active
October 18, 2018 16:27
-
-
Save pablomayobre/c7b7ad81eb02b3f79a349cb3ad2c74a4 to your computer and use it in GitHub Desktop.
1K - Tic Tac Toe in LÖVE (with AI)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local bit = require 'bit' --Bitops | |
local max_int = bit.bnot(0) --Max integer | |
--Players are represented as 9bit numbers | |
local players = {0,0} | |
--Winner (1 - player 1, 2 - player 2, 3 - tie, nil - still playing) | |
local winner = nil | |
local tile_size = 200 --Tile width and height | |
love.window.setMode(tile_size * 3, tile_size * 3) | |
local function is_empty_tile (tile) | |
return bit.bor(bit.bnot(tile), players[1], players[2]) ~= max_int | |
end | |
--Win conditions | |
local win_conditions = {7, 56, 73, 84, 146, 273, 292, 448} | |
for index, win in ipairs(win_conditions) do | |
win_conditions[index] = bit.bnot(win) | |
end | |
local function is_win_move (player_index, tile) | |
for _, win in ipairs(win_conditions) do | |
if bit.bor(win, players[player_index], tile) == max_int then | |
return true | |
end | |
end | |
return false | |
end | |
local function has_won (player_index) | |
if not winner and is_win_move(player_index, 0) then | |
winner = player_index | |
end | |
end | |
local full_board = 2^9 - 1 | |
local function is_board_full () | |
return bit.bor(players[1], players[2]) == full_board | |
end | |
--AI score for tiles, the order should be corners, center and then other places | |
local base_tile_score = {9,4,8,3,5,2,7,1,6} | |
local function ai_turn() | |
local tile_score, tile = 0, 0 | |
for i=0, 8 do | |
local current_tile = 2^i | |
if is_empty_tile(current_tile) then | |
local current_score = base_tile_score[i+1] | |
--Prioritize win movement | |
current_score = current_score + (is_win_move(2, current_tile) and 11 or 0) | |
--Prioritize a move that stops the player from winning | |
current_score = current_score + (is_win_move(1, current_tile) and 10 or 0) | |
if current_score > tile_score then | |
tile_score, tile = current_score, current_tile | |
end | |
end | |
end | |
players[2] = bit.bor(tile, players[2]) | |
has_won(2) --Check if AI won | |
end | |
local function get_tile_bit (x, y) | |
y = math.floor(y/tile_size) | |
x = math.floor(x/tile_size) | |
local tile = y * 3 + x | |
return 2 ^ tile --bit.lshift(1, tile) | |
end | |
function love.mousepressed (x, y) | |
--Someone won so after a click we reset the game | |
if winner then | |
players[1], players[2] = 0,0 | |
winner = nil | |
return | |
end | |
--Turn mouse position into a bit representing the tile | |
local tile = get_tile_bit(x, y) | |
if is_empty_tile(tile) then | |
players[1] = bit.bor(players[1], tile) --Mark the tile for this player | |
has_won(1) --Check if player won | |
if not winner then | |
ai_turn() --AI plays | |
end | |
--Tie is when no one has won and board is full | |
winner = (not winner and is_board_full()) and 3 or winner | |
end | |
end | |
local timer, flash = 0, false | |
function love.update (dt) | |
timer = timer + dt | |
--We flash every .3 seconds | |
while timer > .3 do | |
flash = not flash | |
timer = timer - .3 | |
end | |
end | |
local colors = {{255, 0, 0}, {0, 0, 255}, {255, 255, 255}} | |
local function get_color (player_index) | |
if not flash and (winner == player_index or winner == 3) then | |
return colors[3] | |
else | |
return colors[player_index] | |
end | |
end | |
local function draw_cross (cross_size) | |
love.graphics.line(-cross_size, -cross_size, cross_size, cross_size) | |
love.graphics.line(-cross_size, cross_size, cross_size, -cross_size) | |
end | |
function love.draw () | |
local tile_center = tile_size / 2 | |
local marks_size = tile_size * .4 | |
for i=0, 8 do | |
love.graphics.setColor(colors[3]) --White | |
local x, y = (i%3) * tile_size, math.floor(i/3) * tile_size | |
local mask = bit.bnot(2^i) | |
--Draw board | |
love.graphics.rectangle('line', x, y, tile_size, tile_size) --9 rectangles | |
love.graphics.push('all') | |
--Move to the center of the tile | |
love.graphics.translate(x + tile_center,y + tile_center) | |
--Check if tile is marked by Player 1 | |
if bit.bor(mask, players[1]) == max_int then | |
love.graphics.setColor(get_color(1)) | |
draw_cross(marks_size)--Draw Player 1 | |
--Check if tile is marked by Player 2 (AI) | |
elseif bit.bor(mask, players[2]) == max_int then | |
love.graphics.setColor(get_color(2)) | |
love.graphics.circle('line', 0, 0, marks_size) --Draw Player 2 | |
end | |
love.graphics.pop() | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--Note that when I performed the minification | |
--I changed a lots of parts of the code | |
--In order to get into the 1024 characters | |
--Some of those reductions are not performed in this code | |
--This is only a guide to understand how the game works | |
Y = require 'bit' --Bitops | |
a,b,c = Y.bnot,Y.bor,Y.band --Operations | |
m = a(0) --Max integer | |
F = math.floor | |
w = {7, 56, 73, 84, 146, 273, 292, 448} --Win conditions | |
C = {255,255,255} --Colors | |
A = {9,4,8,3,5,2,7,1,6} --Score for tiles | |
p = {0,0} --Players | |
X = nil --Winner (1 - player 1, 2 - player 2, 3 - tie, false - still playing) | |
--Love | |
L = love | |
G = L.graphics | |
--Width and Height | |
D = 200 | |
B, E, H = D*3, D/2, D*.4 | |
L.window.setMode(B,B) | |
--Flashing | |
v = 0 | |
M = 1 | |
L.mousepressed = function(x, y) | |
if X then --Someone won so after a click we reset the game | |
p = {0,0} | |
X = nil | |
t = 1 | |
return | |
end | |
T = 2^(F(y/D)*3 + F(x/D)) --Tile | |
if b(a(T), p[1], p[2]) ~= m then --Empty tile | |
p[1] = b(p[1], T) --Mark player | |
Z(1) --Check if player won | |
if not X then | |
O, P = 0, 0 | |
for i=0, 8 do | |
Q, R = 2^i, 0 | |
if b(a(Q), p[1], p[2]) ~= m then | |
R = A[i+1] --The order should be corners, center and then other places | |
for j=1, 8 do | |
g = a(w[j]) | |
--Prioritize win movement, or a movement that prevent the player from winning | |
R = R + (b(g, p[1], Q)==m and 10 or 0) + (b(g, p[2], Q)==m and 11 or 0) | |
end | |
if R > O then | |
O, P = R, Q | |
end | |
end | |
end | |
p[2]=b(P,p[2]) | |
Z(2) --Check if AI won | |
end | |
--Tie is when no one has one and board is full | |
X = (not X and (b(p[1], p[2]) + 1 == 2^9)) and 3 or X | |
end | |
end | |
Z = function (N) | |
for i=1, 8 do | |
f = a(w[i]) --Win condition | |
--If the win condition is matched then player wins | |
X = (not X and b(f, p[N])==m) and N or X | |
end | |
end | |
L.update = function (dt) | |
v = v + dt --We just make M flash every .3 seconds | |
if v > .3 then | |
M = not M | |
v = 0 | |
end | |
end | |
L.draw = function () | |
for i=0, 8 do | |
G.setColor(255,255,255) --White | |
x, y, n = (i%3)*D, F(i/3)*D, a(2^i) | |
--Draw board | |
G.rectangle('line', x, y, D, D) --9 rectangles | |
G.push('all') | |
G.translate(x+E,y+E) --The center of the tile | |
if b(n, p[1]) == m then | |
--M is flash, so if player 1 wins or there is a tie we flash the pieces | |
G.setColor((not M and(X==1 or X==3))and C or{255,0,0}) | |
--Draw player 1 | |
G.line(-H, -H, H, H) --Cross | |
G.line(-H, H, H, -H) | |
elseif b(n, p[2]) == m then | |
--M is flash, so if player 2 wins or there is a tie we flash the pieces | |
G.setColor((not M and(X==2 or X==3))and C or{0,0,255}) | |
--Draw player 2 | |
G.circle('line', 0, 0, H) --Circle | |
end | |
G.pop() | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Y,F,w,C,A,p,k,L,D,v,M=require'bit',math.floor,{7,56,73,84,146,273,292,448},{255,255,255},{9,4,8,3,5,2,7,1,6},0,0,love,200,0,1;q,m,b,G,B,Z,L.mousepressed,L.update,L.draw='line',Y.bnot(0),Y.bor,L.graphics,600,function(N)f=N==1 and p or k;for i=1,8 do X=not X and m==b(m-w[i],f)and N or X end end,function(x,y)if X then p,k,X=0,0;return end;T=2^(F(y/D)*3+F(x/D))if m~=b(m-T,p,k)then p=b(p,T)Z(1)if not X then O,P=0,0;for i=0,8 do Q=2^i;if m~=b(m-Q,p,k)then R=A[i+1]for j=1,8 do S=m-w[j]R=R+(m==b(S,p,Q)and 10 or 0)+(m==b(S,k,Q)and 11 or 0)end;if R>O then O,P=R,Q end end end;k=b(P,k)Z(2)end;X=not X and 2^9-1==b(p,k)and 3 or X end end,function(t)v=v+t;if v>.3 then v,M=0,not M end end,function()for i=0,8 do n,x,y=m-2^i,D*(i%3),D*F(i/3)o(C)G.rectangle(q,x,y,D,D)if m==b(n,p)then o((M and(X==1 or X==3))and C or{255,0,0})J,H,r,s=20+x,180+x,20+y,180+y;e(J,r,H,s)e(J,s,H,r)elseif m==b(n,k)then o((not M and(X==2 or X==3))and C or{0,0,255})G.circle(q,x+100,y+100,90)end end end;L.window.setMode(B,B)e,o=G.line,G.setColor |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment