From 98048a1d837b7c0a42682a5139c7fcba35939e99 Mon Sep 17 00:00:00 2001 From: Jan-Eric Nitschke <47750513+JanEricNitschke@users.noreply.github.com> Date: Sun, 23 Jul 2023 21:27:11 +0200 Subject: [PATCH] Added two player version for lua --- .github/workflows/lua.yaml | 34 ++++++++ .gitignore | 1 + tictactoe_lua/main.lua | 3 + tictactoe_lua/tests/test_tictactoe.lua | 61 ++++++++++++++ tictactoe_lua/tictactoe_lua.lua | 112 +++++++++++++++++++++++++ 5 files changed, 211 insertions(+) create mode 100644 .github/workflows/lua.yaml create mode 100644 tictactoe_lua/main.lua create mode 100644 tictactoe_lua/tests/test_tictactoe.lua create mode 100644 tictactoe_lua/tictactoe_lua.lua diff --git a/.github/workflows/lua.yaml b/.github/workflows/lua.yaml new file mode 100644 index 0000000..6b64941 --- /dev/null +++ b/.github/workflows/lua.yaml @@ -0,0 +1,34 @@ +# This workflow will run the tests on the lua version of tictactoe + +name: lua + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./tictactoe_lua + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - uses: actions/checkout@v3 + with: + repository: bluebird75/luaunit + path: luaunit + clean: false + sparse-checkout: | + luaunit.lua + sparse-checkout-cone-mode: false + - uses: leafo/gh-actions-lua@v10 + with: + luaVersion: "5.4" + - name: Test + run: | + mv ../luaunit/luaunit.lua . + lua tests/test_tictactoe.lua diff --git a/.gitignore b/.gitignore index fe0b915..fcf52d3 100644 --- a/.gitignore +++ b/.gitignore @@ -182,6 +182,7 @@ tictactoe_ruby/*.gem *.class /fibonacci +luaunit.lua tictactoe_java/*.jar diff --git a/tictactoe_lua/main.lua b/tictactoe_lua/main.lua new file mode 100644 index 0000000..61d8b3a --- /dev/null +++ b/tictactoe_lua/main.lua @@ -0,0 +1,3 @@ +require("tictactoe_lua") + +TicTacToe.play() diff --git a/tictactoe_lua/tests/test_tictactoe.lua b/tictactoe_lua/tests/test_tictactoe.lua new file mode 100644 index 0000000..b20999c --- /dev/null +++ b/tictactoe_lua/tests/test_tictactoe.lua @@ -0,0 +1,61 @@ +package.path = package.path .. ";../?.lua" +require("tictactoe_lua") + +Lu = require('luaunit') + +TestGeneral = {} + +function TestGeneral:setUp() + TicTacToe.board = { "1", "2", "3", "4", "5", "6", "7", "8", "9" } +end + +function TestGeneral:test_swap_player() + Lu.assertEquals(TicTacToe.swap_player("X"), "O") + Lu.assertEquals(TicTacToe.swap_player("O"), "X") +end + +function TestGeneral:test_is_board_filled() + Lu.assertFalse(TicTacToe.is_board_filled()) + TicTacToe.board = { "X", "X", "X", "O", "O", "O", "O", "X", "X" } + Lu.assertTrue(TicTacToe.is_board_filled()) +end + +function TestGeneral:test_check_win_condition() + TicTacToe.board = { "X", "X", "3", "4", "5", "6", "7", "8", "X" } + Lu.assertEquals(TicTacToe.check_win_condition({ 1, 2, 3 }, "X"), { spots_open = { 3 }, spots_done = 2 }) + Lu.assertEquals(TicTacToe.check_win_condition({ 1, 2, 9 }, "X"), { spots_open = {}, spots_done = 3 }) + Lu.assertEquals(TicTacToe.check_win_condition({ 1, 2, 9 }, "O"), { spots_open = {}, spots_done = 0 }) +end + +function TestGeneral:Test_isplayer_win_row() + TicTacToe.board = {"X", "X", "X", "4", "5", "6", "7", "8", "X"} + Lu.assertFalse(TicTacToe.is_player_win("O")) + Lu.assertTrue(TicTacToe.is_player_win("X")) +end + +function TestGeneral:Test_isplayer_win_col() + TicTacToe.board = {"1", "O", "X", "4", "O", "6", "7", "O", "X"} + Lu.assertFalse(TicTacToe.is_player_win("X")) + Lu.assertTrue(TicTacToe.is_player_win("O")) +end + +function TestGeneral:Test_isplayer_win_diag() + TicTacToe.board = {"1", "2", "X", "4", "X", "6", "X", "O", "O"} + Lu.assertFalse(TicTacToe.is_player_win("O")) + Lu.assertTrue(TicTacToe.is_player_win("X")) +end + +function TestGeneral:test_fix_spot() + TicTacToe.board = {"1", "2", "X", "4", "X", "6", "X", "O", "O"} + Lu.assertTrue(TicTacToe.fix_spot(1, "X")) + -- Now occupied + Lu.assertFalse(TicTacToe.fix_spot(1, "X")) + -- Too small + Lu.assertFalse(TicTacToe.fix_spot(-3, "O")) + -- Too big + Lu.assertFalse(TicTacToe.fix_spot(13, "O")) +end + + + +os.exit(Lu.LuaUnit.run()) diff --git a/tictactoe_lua/tictactoe_lua.lua b/tictactoe_lua/tictactoe_lua.lua new file mode 100644 index 0000000..05a3e4c --- /dev/null +++ b/tictactoe_lua/tictactoe_lua.lua @@ -0,0 +1,112 @@ +TicTacToe = { + board = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + win_conditions = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 }, -- Rows + { 1, 4, 7 }, { 2, 5, 8 }, { 3, 5, 9 }, -- Cols + { 1, 5, 9 }, { 3, 5, 7 } -- Diags + }, + player_markers = { X = true, O = true }, + ai_opponent = false, + ai_marker = "X", + ai_strength = 0, +} + +function TicTacToe.swap_player(current_player) + if current_player == "X" then + return "O" + end + return "X" +end + +function TicTacToe.show_board() + local line_separator = "---------------" + local side_length = 3 + print(line_separator) + for row = 1, side_length do + for col = 1, side_length do + io.write("| " .. TicTacToe.board[(row - 1) * side_length + col] .. " |") + end + print "" + print(line_separator) + end +end + +function TicTacToe.is_board_filled() + for _, value in ipairs(TicTacToe.board) do + if TicTacToe.player_markers[value] == nil then + return false + end + end + return true +end + +function TicTacToe.check_win_condition(win_condition, current_player) + local result = { spots_open = {}, spots_done = 0 } + for _, spot in ipairs(win_condition) do + if TicTacToe.board[spot] == current_player then + result.spots_done = result.spots_done + 1 + -- If it is not the current player and it is not the other one + -- Then it is open + elseif TicTacToe.board[spot] ~= TicTacToe.swap_player(current_player) then + table.insert(result.spots_open, spot) + end + end + return result +end + +function TicTacToe.is_player_win(current_player) + for _, win_condition in pairs(TicTacToe.win_conditions) do + local result = TicTacToe.check_win_condition(win_condition, current_player) + if result.spots_done == 3 then + return true + end + end + return false +end + +function TicTacToe.fix_spot(spot, current_player) + if TicTacToe.board[spot] == nil then + print("ERROR: Spot has to be in range [1-9]!") + return false + end + if TicTacToe.player_markers[TicTacToe.board[spot]] ~= nil then + print("ERROR: Spot " .. spot .. " is already occupied!") + return false + end + TicTacToe.board[spot] = current_player + return true +end + +function TicTacToe.player_turn(current_player) + while true do + print("Player " .. current_player .. " turn.") + TicTacToe.show_board() + local result = io.read("n") + if result == nil then + print("ERROR: Input must be a valid integer!") + io.read("l") + goto continue + end + if TicTacToe.fix_spot(result, current_player) then + break + end + ::continue:: + end +end + +function TicTacToe.play() + local player = "X" + + while true do + TicTacToe.player_turn(player) + if TicTacToe.is_player_win(player) then + print("Player " .. player .. " wins the game!") + break + end + if TicTacToe.is_board_filled() then + print("Draw!") + break + end + player = TicTacToe.swap_player(player) + end + TicTacToe.show_board() +end