-- title: Stacked Tic-Tac-Toe -- author: Rokas Puzonas -- desc: Standard tic tac toe, but you can stack certain pieces on top of others -- site: https://github.com/RokasPuzonas/stacked-tic-tac-toe -- license: MIT License -- script: lua local DISPLAY_WIDTH = 240 local DISPLAY_HEIGHT = 136 local CELL_SIZE = 33 local CELL_GAP = 3 local BOARD_SIZE = 3*CELL_SIZE + 2*CELL_GAP local SIDE_PANEL_WIDTH = 60 local P1_COLOR = 3 local P2_COLOR = 10 local p1_turn = true local board = {0, 0, 0, 0, 0, 0, 0, 0, 0} local p1_pieces = {1, 1, 2, 2, 3, 3} local p2_pieces = {1, 1, 2, 2, 3, 3} local is_dragging_piece = false local dragging_piece_size = 0 local was_mouse_down = false local finished = false local stalemate = false local function contains(t, v) for _, value in pairs(t) do if value == v then return true end end return false end local function countValue(t, v) local count = 0 for _, value in pairs(t) do if value == v then count = count + 1 end end return count end local function sign(x) if x > 0 then return 1 elseif x < 0 then return -1 else return 0 end end local function removeValue(t, v) for i, value in ipairs(t) do if value == v then table.remove(t, i) break end end end local function drawPiece(x, y, size, color) local outer_size if size == 3 then outer_size = CELL_SIZE*0.4 elseif size == 2 then outer_size = CELL_SIZE*0.3 else outer_size = CELL_SIZE*0.2 end circ(x, y, outer_size, 15) circ(x, y, outer_size*0.7, color) end local function drawGrid(x, y) local grid_color = 14 rect( x + CELL_SIZE - CELL_GAP/2, y, CELL_GAP, BOARD_SIZE, grid_color ) rect( x + 2*CELL_SIZE + CELL_GAP/2, y, CELL_GAP, BOARD_SIZE, grid_color ) rect( x, y + CELL_SIZE - CELL_GAP/2, BOARD_SIZE, CELL_GAP, grid_color ) rect( x, y + 2*CELL_SIZE + CELL_GAP/2, BOARD_SIZE, CELL_GAP, grid_color ) end local function drawBoard(x, y) drawGrid(x, y) for i, piece_size in ipairs(board) do if piece_size == 0 then goto continue end local color if piece_size > 0 then color = P1_COLOR else color = P2_COLOR end local marker_x = (i-1) % 3 local marker_y = math.floor((i-1) / 3) drawPiece( x + (marker_x * (CELL_SIZE + CELL_GAP) - CELL_GAP) + CELL_SIZE/2, y + (marker_y * (CELL_SIZE + CELL_GAP) - CELL_GAP) + CELL_SIZE/2, math.abs(piece_size), color ) ::continue:: end end local function drawPlayerSidePanel(panel_x, panel_y, color, pieces) for i=3, 1, -1 do if contains(pieces, i) then local x = panel_x + (SIDE_PANEL_WIDTH-CELL_SIZE)/2 local y = panel_y + (((3 - i)) * (CELL_SIZE + CELL_GAP) - CELL_GAP) drawPiece(x + CELL_SIZE/2, y + CELL_SIZE/2, math.abs(i), color) local count = countValue(pieces, i) print(("%dx"):format(count), x-10, y+CELL_SIZE/2-3) end end end local function drawTurnLabel() local x = (DISPLAY_WIDTH-80)/2 local y = 4 print("Player turn", x, y) if p1_turn then rect(x+38, y+1, 5, 5, P1_COLOR) else rect(x+38, y+1, 5, 5, P2_COLOR) end end local function drawWinningLabel() local x = (DISPLAY_WIDTH-80)/2 local y = 4 print("Player won", x, y) if p1_turn then rect(x+38, y+1, 5, 5, P1_COLOR) else rect(x+38, y+1, 5, 5, P2_COLOR) end end local function isInRect(target_x, target_y, x, y, w, h) return target_x >= x and target_x < x + w and target_y >= y and target_y < y + h end local function didCurrentPlayerWin() local player_sign if p1_turn then player_sign = 1 else player_sign = -1 end local player_board = {} for _, piece in ipairs(board) do table.insert(player_board, sign(piece) == player_sign) end for row=0,6,3 do if player_board[row+1] and player_board[row+2] and player_board[row+3] then return true end end for col=0,2 do if player_board[col+0] and player_board[col+3] and player_board[col+6] then return true end end return (player_board[1] and player_board[5] and player_board[9]) or (player_board[3] and player_board[5] and player_board[7]) end local function areThereAvailableMoves() local pieces local player_sign if p1_turn then pieces = p1_pieces player_sign = 1 else player_sign = -1 pieces = p2_pieces end local highest_piece = 0 for _, piece in ipairs(pieces) do highest_piece = math.max(highest_piece, piece) end for _, piece in ipairs(board) do if highest_piece > math.abs(piece) and sign(piece) == -player_sign or sign(piece) == 0 then return true end end return false end local function drawDisplayCorners() local corner_size = 20 tri(0, 0, corner_size, 0, 0, corner_size, 0) tri(DISPLAY_WIDTH, 0, DISPLAY_WIDTH-corner_size, 0, DISPLAY_WIDTH, corner_size, 0) tri(0, DISPLAY_HEIGHT, corner_size, DISPLAY_HEIGHT, 0, DISPLAY_HEIGHT-corner_size, 0) tri(DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_WIDTH-corner_size, DISPLAY_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT-corner_size, 0) end function TIC() cls(13) drawDisplayCorners() local board_x = (DISPLAY_WIDTH-BOARD_SIZE)/2 local board_y = (DISPLAY_HEIGHT-BOARD_SIZE)/2 drawBoard(board_x, board_y) if stalemate then print("Draw!", (DISPLAY_WIDTH-30)/2, 4) elseif not finished then drawTurnLabel() else drawWinningLabel() end if finished or stalemate then print("Ctrl+R to restart", (DISPLAY_WIDTH-100)/2, DISPLAY_HEIGHT-10) end local side_panel_gap = (DISPLAY_WIDTH-BOARD_SIZE-2*SIDE_PANEL_WIDTH)/4 local side_panel_y = board_y local p1_side_panel_x = side_panel_gap local p2_side_panel_x = DISPLAY_WIDTH-SIDE_PANEL_WIDTH-side_panel_gap drawPlayerSidePanel(p1_side_panel_x, board_y, P1_COLOR, p1_pieces) drawPlayerSidePanel(p2_side_panel_x, board_y, P2_COLOR, p2_pieces) local mx, my, is_mouse_down = mouse() if not finished then if not was_mouse_down and is_mouse_down then local pieces local side_panel_x if p1_turn then pieces = p1_pieces side_panel_x = p1_side_panel_x else pieces = p2_pieces side_panel_x = p2_side_panel_x end local piece_size = 3-math.floor((my - side_panel_y) / (CELL_SIZE+CELL_GAP)) if isInRect(mx, my, side_panel_x, side_panel_y, SIDE_PANEL_WIDTH, BOARD_SIZE) and countValue(pieces, piece_size) > 0 then dragging_piece_size = piece_size is_dragging_piece = true end elseif was_mouse_down and not is_mouse_down then if is_dragging_piece and isInRect(mx, my, board_x, board_y, BOARD_SIZE, BOARD_SIZE) then local piece_x = math.floor((mx - board_x) / (CELL_SIZE+CELL_GAP)) local piece_y = math.floor((my - board_y) / (CELL_SIZE+CELL_GAP)) local board_index = 1+piece_x + piece_y * 3 if math.abs(board[board_index]) < dragging_piece_size then if p1_turn then removeValue(p1_pieces, dragging_piece_size) board[board_index] = dragging_piece_size else removeValue(p2_pieces, dragging_piece_size) board[board_index] = -dragging_piece_size end if didCurrentPlayerWin() then finished = true else p1_turn = not p1_turn end if not areThereAvailableMoves() then stalemate = true end end end is_dragging_piece = false end was_mouse_down = is_mouse_down if is_dragging_piece then if p1_turn then drawPiece(mx, my, dragging_piece_size, P1_COLOR) else drawPiece(mx, my, dragging_piece_size, P2_COLOR) end end end end -- -- 000:00000000000000000000000000000fff00000fff0000ffff0000ffff000fffff -- 001:00000000000000000ffffffffffffffffffffffffffffffffffff333f3333333 -- 002:0000000000000000fffffff0ffffffffffffffffffffffff3333ffff33333fff -- 003:00000000000000000000000000000000f0000000ff000000fff00000fff00000 -- 004:000000000000000000000000000000000000000000000000000000000000000f -- 005:000000000000000000000000000000000000ffff0fffffff0fffffffffffff33 -- 006:00000000000000000000000000000000fffffff0ffffffffffffffff3333ffff -- 007:000000000000000000000000000000000000000000000000f0000000ff000000 -- 010:00000000000000000000000000000000000000000000000000000000fffff000 -- 016:000fffff000fffff00fffff300fffff300fffff300ffff330fffff330fffff33 -- 017:3333333333333333333333333333333333333333333333333333333333333333 -- 018:33333fff333333ff3333333f3333333f33333333333333333333333333333333 -- 019:ffff0000fffff000fffff000fffff000fffff0003fffff003fffff003fffff00 -- 020:000000ff000000ff000000ff000000ff00000fff00000fff00000fff00000fff -- 021:ffff3333ffff3333ff333333f3333333f3333333f3333333f333333333333333 -- 022:33333fff333333ff3333333f3333333f33333333333333333333333333333333 -- 023:ff000000ff000000fff00000fff00000fff00000fff00000fff00000fff00000 -- 025:0000ffff00ffffff0ffffffffffff333ffff3333fff33333fff33333fff33333 -- 026:fffffff0ffffffffffffffff3333ffff33333fff33333fff333333ff333333ff -- 027:00000000000000000000000000000000f0000000f0000000f0000000f0000000 -- 032:0fffff330fffff330fffff330ffffff300fffff300ffffff00ffffff000fffff -- 033:3333333333333333333333333333333333333333f3333333ff333333ffff3333 -- 034:3333333333333333333333333333333333333333333333333333333f33333fff -- 035:3fffff0033ffff0033ffff0033ffff003fffff003fffff00ffffff00fffff000 -- 036:00000fff00000fff00000fff00000fff00000fff00000fff000000ff0000000f -- 037:33333333333333333333333333333333f3333333fff33333ffffff33ffffffff -- 038:33333333333333333333333f3333333f333333ff33333fff3333ffffffffffff -- 039:fff00000fff00000fff00000fff00000ff000000ff000000ff000000f0000000 -- 041:fff33333ffff33330fffff330fffffff0fffffff000fffff0000000000000000 -- 042:33333fff33333fff33fffffffffffffffffffff0fffff0000000000000000000 -- 043:f0000000f0000000f00000000000000000000000000000000000000000000000 -- 048:0000ffff00000fff000000ff0000000000000000000000000000000000000000 -- 049:ffffff33ffffffffffffffffffffffff00ffffff00000fff0000000000000000 -- 050:3333ffff33ffffffffffffffffffffffffffffffffffff000000000000000000 -- 051:fffff000ffff0000ff000000f000000000000000000000000000000000000000 -- 053:0fffffff000fffff000000000000000000000000000000000000000000000000 -- 054:ffffffffffffff00000000000000000000000000000000000000000000000000 -- 064:0000000000000000000000000000000f0000000f000000ff00000fff0000ffff -- 065:00000000000000000ffffffffffffffffffffffffffffffffffff33fff333333 -- 066:0000000000000000ffff0000ffffff00fffffff0ffffffffffffffff33ffffff -- 067:000000000000000000000000000000000000000000000000f0000000f0000000 -- 080:000fffff000fffff00fffff300fffff300fffff300ffff330fffff330ffff333 -- 081:f333333333333333333333333333333333333333333333333333333333333333 -- 082:333fffff3333ffff33333fff333333ff3333333f333333333333333333333333 -- 083:ff000000fff00000ffff0000ffff0000ffff0000fffff000fffff000fffff000 -- 096:0ffff3330ffff3330ffff3330ffff3330fffff330ffffff300ffffff00ffffff -- 097:33333333333333333333333333333333333333333333333333333333f3333333 -- 098:33333333333333333333333333333333333333333333333f33333fff3333ffff -- 099:3fffff003fffff003fffff00fffff000fffff000fffff000ffff0000ffff0000 -- 112:000fffff000fffff0000ffff00000fff000000ff000000000000000000000000 -- 113:ff333333ffff3333ffffffffffffffffffffffffffffffff0000000000000000 -- 114:3fffffff3ffffffffffffffffffffff0ffff0000ffff00000000000000000000 -- 115:fff00000ff000000f00000000000000000000000000000000000000000000000 -- -- -- 000:00000000ffffffff00000000ffffffff -- 001:0123456789abcdeffedcba9876543210 -- 002:0123456789abcdef0123456789abcdef -- -- -- 000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000304000000000 -- -- -- 000:100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- -- -- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57 --