From c34d7363b61a9e00c986b79793bf7cdc03e9ea99 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Mon, 4 Mar 2024 09:15:01 +0100 Subject: wip --- tictactoe-2/.gitignore | 20 +++++ tictactoe-2/apps/tictactoe/src/tictactoe.app.src | 15 ++++ tictactoe-2/apps/tictactoe/src/tictactoe_app.erl | 18 +++++ tictactoe-2/apps/tictactoe/src/tictactoe_sup.erl | 35 ++++++++ tictactoe-2/apps/ttt/rebar.config | 7 ++ tictactoe-2/apps/ttt/rebar.lock | 1 + tictactoe-2/apps/ttt/src/ttt.app.src | 13 +++ tictactoe-2/apps/ttt/src/ttt.erl | 51 ++++++++++++ tictactoe-2/apps/ttt/test/ttt_test.erl | 18 +++++ tictactoe-2/apps/ttt_server/.gitignore | 20 +++++ tictactoe-2/apps/ttt_server/rebar.config | 2 + tictactoe-2/apps/ttt_server/rebar.lock | 1 + tictactoe-2/apps/ttt_server/src/ttt_server.app.src | 14 ++++ tictactoe-2/apps/ttt_server/src/ttt_server.erl | 93 ++++++++++++++++++++++ tictactoe-2/apps/ttt_server/test/server_tests.erl | 14 ++++ tictactoe-2/config/sys.config | 3 + tictactoe-2/config/vm.args | 6 ++ tictactoe-2/rebar.config | 32 ++++++++ tictactoe-2/rebar.lock | 1 + tictactoe/ttt/rebar.config | 7 ++ tictactoe/ttt/rebar.lock | 1 + tictactoe/ttt/src/ttt.app.src | 13 +++ tictactoe/ttt/src/ttt.erl | 51 ++++++++++++ tictactoe/ttt/test/ttt_test.erl | 18 +++++ ttt/rebar.config | 7 -- ttt/rebar.lock | 1 - ttt/src/ttt.app.src | 13 --- ttt/src/ttt.erl | 51 ------------ ttt/test/ttt_test.erl | 18 ----- 29 files changed, 454 insertions(+), 90 deletions(-) create mode 100644 tictactoe-2/.gitignore create mode 100644 tictactoe-2/apps/tictactoe/src/tictactoe.app.src create mode 100644 tictactoe-2/apps/tictactoe/src/tictactoe_app.erl create mode 100644 tictactoe-2/apps/tictactoe/src/tictactoe_sup.erl create mode 100644 tictactoe-2/apps/ttt/rebar.config create mode 100644 tictactoe-2/apps/ttt/rebar.lock create mode 100644 tictactoe-2/apps/ttt/src/ttt.app.src create mode 100644 tictactoe-2/apps/ttt/src/ttt.erl create mode 100644 tictactoe-2/apps/ttt/test/ttt_test.erl create mode 100644 tictactoe-2/apps/ttt_server/.gitignore create mode 100644 tictactoe-2/apps/ttt_server/rebar.config create mode 100644 tictactoe-2/apps/ttt_server/rebar.lock create mode 100644 tictactoe-2/apps/ttt_server/src/ttt_server.app.src create mode 100644 tictactoe-2/apps/ttt_server/src/ttt_server.erl create mode 100644 tictactoe-2/apps/ttt_server/test/server_tests.erl create mode 100644 tictactoe-2/config/sys.config create mode 100644 tictactoe-2/config/vm.args create mode 100644 tictactoe-2/rebar.config create mode 100644 tictactoe-2/rebar.lock create mode 100644 tictactoe/ttt/rebar.config create mode 100644 tictactoe/ttt/rebar.lock create mode 100644 tictactoe/ttt/src/ttt.app.src create mode 100644 tictactoe/ttt/src/ttt.erl create mode 100644 tictactoe/ttt/test/ttt_test.erl delete mode 100644 ttt/rebar.config delete mode 100644 ttt/rebar.lock delete mode 100644 ttt/src/ttt.app.src delete mode 100644 ttt/src/ttt.erl delete mode 100644 ttt/test/ttt_test.erl diff --git a/tictactoe-2/.gitignore b/tictactoe-2/.gitignore new file mode 100644 index 0000000..df53f7d --- /dev/null +++ b/tictactoe-2/.gitignore @@ -0,0 +1,20 @@ +.rebar3 +_build +_checkouts +_vendor +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +.idea +*.iml +rebar3.crashdump +*~ diff --git a/tictactoe-2/apps/tictactoe/src/tictactoe.app.src b/tictactoe-2/apps/tictactoe/src/tictactoe.app.src new file mode 100644 index 0000000..ccce597 --- /dev/null +++ b/tictactoe-2/apps/tictactoe/src/tictactoe.app.src @@ -0,0 +1,15 @@ +{application, tictactoe, + [{description, "An OTP application"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, {tictactoe_app, []}}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache-2.0"]}, + {links, []} + ]}. diff --git a/tictactoe-2/apps/tictactoe/src/tictactoe_app.erl b/tictactoe-2/apps/tictactoe/src/tictactoe_app.erl new file mode 100644 index 0000000..8829237 --- /dev/null +++ b/tictactoe-2/apps/tictactoe/src/tictactoe_app.erl @@ -0,0 +1,18 @@ +%%%------------------------------------------------------------------- +%% @doc tictactoe public API +%% @end +%%%------------------------------------------------------------------- + +-module(tictactoe_app). + +-behaviour(application). + +-export([start/2, stop/1]). + +start(_StartType, _StartArgs) -> + tictactoe_sup:start_link(). + +stop(_State) -> + ok. + +%% internal functions diff --git a/tictactoe-2/apps/tictactoe/src/tictactoe_sup.erl b/tictactoe-2/apps/tictactoe/src/tictactoe_sup.erl new file mode 100644 index 0000000..c506caf --- /dev/null +++ b/tictactoe-2/apps/tictactoe/src/tictactoe_sup.erl @@ -0,0 +1,35 @@ +%%%------------------------------------------------------------------- +%% @doc tictactoe top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(tictactoe_sup). + +-behaviour(supervisor). + +-export([start_link/0]). + +-export([init/1]). + +-define(SERVER, ?MODULE). + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%% sup_flags() = #{strategy => strategy(), % optional +%% intensity => non_neg_integer(), % optional +%% period => pos_integer()} % optional +%% child_spec() = #{id => child_id(), % mandatory +%% start => mfargs(), % mandatory +%% restart => restart(), % optional +%% shutdown => shutdown(), % optional +%% type => worker(), % optional +%% modules => modules()} % optional +init([]) -> + SupFlags = #{strategy => one_for_all, + intensity => 0, + period => 1}, + ChildSpecs = [], + {ok, {SupFlags, ChildSpecs}}. + +%% internal functions diff --git a/tictactoe-2/apps/ttt/rebar.config b/tictactoe-2/apps/ttt/rebar.config new file mode 100644 index 0000000..59700d5 --- /dev/null +++ b/tictactoe-2/apps/ttt/rebar.config @@ -0,0 +1,7 @@ +{erl_opts, [debug_info]}. +{deps, [ +]}. + +{shell, [ + {apps, [ttt]} +]}. diff --git a/tictactoe-2/apps/ttt/rebar.lock b/tictactoe-2/apps/ttt/rebar.lock new file mode 100644 index 0000000..57afcca --- /dev/null +++ b/tictactoe-2/apps/ttt/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/tictactoe-2/apps/ttt/src/ttt.app.src b/tictactoe-2/apps/ttt/src/ttt.app.src new file mode 100644 index 0000000..813faba --- /dev/null +++ b/tictactoe-2/apps/ttt/src/ttt.app.src @@ -0,0 +1,13 @@ +{application, ttt, + [{description, "TTT app"}, + {vsn, "0.1.0"}, + {registered, []}, +% {mod, {myapp_app, []}}, + {applications, + [kernel, stdlib]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache-2.0"]}, + {links, []} + ]}. diff --git a/tictactoe-2/apps/ttt/src/ttt.erl b/tictactoe-2/apps/ttt/src/ttt.erl new file mode 100644 index 0000000..a9fc4d1 --- /dev/null +++ b/tictactoe-2/apps/ttt/src/ttt.erl @@ -0,0 +1,51 @@ +-module(ttt). + +-export_type([ + player/0, + square/0, + board/0, + game_result/0]). + +-export([ + who_wins/1, + empty_board/0, + move/4, + format/1]). + +-type player() :: 'X' | 'O'. +-type square() :: player() | 'E'. +-type board() :: list(square()). +-type game_result() :: player() | 'draw' | 'running'. + +-define(EMPTY_BOARD, ['E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E']). + +-spec empty_board() -> board(). +empty_board() -> ?EMPTY_BOARD. + +-spec who_wins(Board :: board()) -> game_result(). +who_wins([A, A, A, _, _, _, _, _, _]) when A == 'X' orelse A == 'O' -> A; +who_wins([_, _, _, A, A, A, _, _, _]) when A == 'X' orelse A == 'O' -> A; +who_wins([_, _, _, _, _, _, A, A, A]) when A == 'X' orelse A == 'O' -> A; +who_wins([A, _, _, A, _, _, A, _, _]) when A == 'X' orelse A == 'O' -> A; +who_wins([_, A, _, _, A, _, _, A, _]) when A == 'X' orelse A == 'O' -> A; +who_wins([_, _, A, _, _, A, _, _, A]) when A == 'X' orelse A == 'O' -> A; +who_wins([A, _, _, _, A, _, _, _, A]) when A == 'X' orelse A == 'O' -> A; +who_wins([_, _, A, _, A, _, A, _, _]) when A == 'X' orelse A == 'O' -> A; +who_wins(Board) -> + case lists:member('E', Board) of + true -> running; + false -> draw + end. + +move(_, _, Row, _) when Row < 0 orelse Row > 2 -> {bad_arg}; +move(_, _, _, Col) when Col < 0 orelse Col > 2 -> {bad_arg}; +move(_, Move, _, _) when not (Move == 'X') -> {bad_arg}; +move(Board, Move, Row, Col) -> + I = Row * 3 + Col, + Updated = lists:sublist(Board, I) ++ [Move] ++ lists:nthtail(I + 1, Board), + {ok, Updated}. + +format(Board) when length(Board) == 9 -> + B = lists:map(fun(C) -> case C of 'E' -> ' '; _ -> C end end, Board), + io:format("+---+~n|~s~s~s|~n|~s~s~s|~n|~s~s~s|~n+---+~n", B); +format(_) -> "bad board". diff --git a/tictactoe-2/apps/ttt/test/ttt_test.erl b/tictactoe-2/apps/ttt/test/ttt_test.erl new file mode 100644 index 0000000..95b6bf7 --- /dev/null +++ b/tictactoe-2/apps/ttt/test/ttt_test.erl @@ -0,0 +1,18 @@ +-module(ttt_test). + +-include_lib("eunit/include/eunit.hrl"). + +empty_board_test() -> + E = ttt:empty_board(), + ?assertEqual(E, ttt:empty_board()). + +moves_board_test() -> + B0 = ttt:empty_board(), + {ok, B1} = ttt:move(B0, 'X', 0, 0), + {ok, B2} = ttt:move(B1, 'X', 0, 1), + {ok, B3} = ttt:move(B2, 'X', 0, 2), + ?assertEqual(['X', 'X', 'X', 'E', 'E', 'E', 'E', 'E', 'E'], B3). + +simple_test() -> + R = ttt:who_wins(['X', 'X', 'X', 'E', 'E', 'E', 'E', 'E', 'E']), + ?assertEqual('X', R). diff --git a/tictactoe-2/apps/ttt_server/.gitignore b/tictactoe-2/apps/ttt_server/.gitignore new file mode 100644 index 0000000..b0d808b --- /dev/null +++ b/tictactoe-2/apps/ttt_server/.gitignore @@ -0,0 +1,20 @@ +_build/default/lib/.rebar3 +_build +_checkouts +_vendor +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +.idea +*.iml +rebar3.crashdump +*~ diff --git a/tictactoe-2/apps/ttt_server/rebar.config b/tictactoe-2/apps/ttt_server/rebar.config new file mode 100644 index 0000000..2656fd5 --- /dev/null +++ b/tictactoe-2/apps/ttt_server/rebar.config @@ -0,0 +1,2 @@ +{erl_opts, [debug_info]}. +{deps, []}. diff --git a/tictactoe-2/apps/ttt_server/rebar.lock b/tictactoe-2/apps/ttt_server/rebar.lock new file mode 100644 index 0000000..57afcca --- /dev/null +++ b/tictactoe-2/apps/ttt_server/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/tictactoe-2/apps/ttt_server/src/ttt_server.app.src b/tictactoe-2/apps/ttt_server/src/ttt_server.app.src new file mode 100644 index 0000000..d3de90f --- /dev/null +++ b/tictactoe-2/apps/ttt_server/src/ttt_server.app.src @@ -0,0 +1,14 @@ +{application, ttt_server, + [{description, "An OTP library"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, + [kernel, + stdlib + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache-2.0"]}, + {links, []} + ]}. diff --git a/tictactoe-2/apps/ttt_server/src/ttt_server.erl b/tictactoe-2/apps/ttt_server/src/ttt_server.erl new file mode 100644 index 0000000..62979e8 --- /dev/null +++ b/tictactoe-2/apps/ttt_server/src/ttt_server.erl @@ -0,0 +1,93 @@ +-module(ttt_server). + +-export([ + start/0, + start_local/0, + stop/0, + stop/2, + loop/1, + + start_game/1, + dump/1]). + +-import(ttt, [move/4, who_wins/1, format/1, empty_board/0]). + +-record(ttt_game, { + ref = make_ref(), + board, + player1, + player2}). + +-record(ttt_state, { + games = []}). + +-define(GLOBAL_NAME, ttt). + +loop(State) -> + _State = + receive + {Player1, start} when is_pid(Player1) -> + Board = ttt:empty_board(), + io:format("board:~n~s~n", [ttt:format(Board)]), + Game = #ttt_game{board = Board, player1 = Player1}, + #ttt_state{games = [Game, State#ttt_state.games]}, + Player1 ! {ok, Game#ttt_game.ref}; + dump -> + io:format("Server state:~n~p~n", [State]), + State; + stop -> + io:format("Exiting~n", []), + exit(normal); + {stop, Pid} -> + Pid ! ok, + exit(normal); + X -> + io:format("unexpected message: ~p~n", [X]), + State + end, + case _State of + _ when _State =/= State -> + io:format("New State: ~p~n", [_State]); + _ -> _State + end, + ?MODULE:loop(_State). + +start_game(Server) -> + Server ! {self(), start}, + receive + {ok, Game} -> + io:format("Game started: ~w~n", [Game]), + {ok, Game}; + X -> + io:format("Game start failed: ~w~n", [X]), + X + after 100 -> + io:format("timeout~n", []), + {timeout} + end. + +dump(Server) -> + Server ! dump. + +start_local() -> + InitialState = #ttt_state{}, + spawn(?MODULE, loop, [InitialState]). + +start() -> + Pid = start_local(), + case global:register_name(?GLOBAL_NAME, Pid) of + yes -> + io:format("Server started, pid=~p~n", [Pid]); + X -> + io:format("Register name failed: ~p~n", [X]), + Pid ! stop + end. + +stop(Server, Pid) -> + Server ! {stop, Pid}. + +stop() -> + case global:whereis_name(?GLOBAL_NAME) of + undefined -> io:format("Server not running~n"); + Pid -> Pid ! {stop, self()} + end. diff --git a/tictactoe-2/apps/ttt_server/test/server_tests.erl b/tictactoe-2/apps/ttt_server/test/server_tests.erl new file mode 100644 index 0000000..322f9ed --- /dev/null +++ b/tictactoe-2/apps/ttt_server/test/server_tests.erl @@ -0,0 +1,14 @@ +-module(server_tests). + +-include_lib("eunit/include/eunit.hrl"). + +simple_test() -> + Server = ttt_server:start_local(), + P1 = ttt_server:start_game(Server), + ?debugFmt("P1:~p~n", [P1]), + ttt_server:dump(Server), + ttt_server:stop(Server, self()), + ?assert(receive + ok -> true + after 100 -> false + end). diff --git a/tictactoe-2/config/sys.config b/tictactoe-2/config/sys.config new file mode 100644 index 0000000..33fcac5 --- /dev/null +++ b/tictactoe-2/config/sys.config @@ -0,0 +1,3 @@ +[ + {tictactoe, []} +]. diff --git a/tictactoe-2/config/vm.args b/tictactoe-2/config/vm.args new file mode 100644 index 0000000..3b90361 --- /dev/null +++ b/tictactoe-2/config/vm.args @@ -0,0 +1,6 @@ +-sname tictactoe + +-setcookie tictactoe_cookie + ++K true ++A30 diff --git a/tictactoe-2/rebar.config b/tictactoe-2/rebar.config new file mode 100644 index 0000000..e08a4e2 --- /dev/null +++ b/tictactoe-2/rebar.config @@ -0,0 +1,32 @@ +{erl_opts, [debug_info]}. +{deps, []}. + +{relx, [{release, {tictactoe, "0.1.0"}, + [tictactoe, + sasl]}, + + {mode, dev}, + + %% automatically picked up if the files + %% exist but can be set manually, which + %% is required if the names aren't exactly + %% sys.config and vm.args + {sys_config, "./config/sys.config"}, + {vm_args, "./config/vm.args"} + + %% the .src form of the configuration files do + %% not require setting RELX_REPLACE_OS_VARS + %% {sys_config_src, "./config/sys.config.src"}, + %% {vm_args_src, "./config/vm.args.src"} +]}. + +{profiles, [{prod, [{relx, + [%% prod is the default mode when prod + %% profile is used, so does not have + %% to be explicitly included like this + {mode, prod} + + %% use minimal mode to exclude ERTS + %% {mode, minimal} + ] + }]}]}. diff --git a/tictactoe-2/rebar.lock b/tictactoe-2/rebar.lock new file mode 100644 index 0000000..57afcca --- /dev/null +++ b/tictactoe-2/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/tictactoe/ttt/rebar.config b/tictactoe/ttt/rebar.config new file mode 100644 index 0000000..59700d5 --- /dev/null +++ b/tictactoe/ttt/rebar.config @@ -0,0 +1,7 @@ +{erl_opts, [debug_info]}. +{deps, [ +]}. + +{shell, [ + {apps, [ttt]} +]}. diff --git a/tictactoe/ttt/rebar.lock b/tictactoe/ttt/rebar.lock new file mode 100644 index 0000000..57afcca --- /dev/null +++ b/tictactoe/ttt/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/tictactoe/ttt/src/ttt.app.src b/tictactoe/ttt/src/ttt.app.src new file mode 100644 index 0000000..813faba --- /dev/null +++ b/tictactoe/ttt/src/ttt.app.src @@ -0,0 +1,13 @@ +{application, ttt, + [{description, "TTT app"}, + {vsn, "0.1.0"}, + {registered, []}, +% {mod, {myapp_app, []}}, + {applications, + [kernel, stdlib]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache-2.0"]}, + {links, []} + ]}. diff --git a/tictactoe/ttt/src/ttt.erl b/tictactoe/ttt/src/ttt.erl new file mode 100644 index 0000000..a9fc4d1 --- /dev/null +++ b/tictactoe/ttt/src/ttt.erl @@ -0,0 +1,51 @@ +-module(ttt). + +-export_type([ + player/0, + square/0, + board/0, + game_result/0]). + +-export([ + who_wins/1, + empty_board/0, + move/4, + format/1]). + +-type player() :: 'X' | 'O'. +-type square() :: player() | 'E'. +-type board() :: list(square()). +-type game_result() :: player() | 'draw' | 'running'. + +-define(EMPTY_BOARD, ['E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E']). + +-spec empty_board() -> board(). +empty_board() -> ?EMPTY_BOARD. + +-spec who_wins(Board :: board()) -> game_result(). +who_wins([A, A, A, _, _, _, _, _, _]) when A == 'X' orelse A == 'O' -> A; +who_wins([_, _, _, A, A, A, _, _, _]) when A == 'X' orelse A == 'O' -> A; +who_wins([_, _, _, _, _, _, A, A, A]) when A == 'X' orelse A == 'O' -> A; +who_wins([A, _, _, A, _, _, A, _, _]) when A == 'X' orelse A == 'O' -> A; +who_wins([_, A, _, _, A, _, _, A, _]) when A == 'X' orelse A == 'O' -> A; +who_wins([_, _, A, _, _, A, _, _, A]) when A == 'X' orelse A == 'O' -> A; +who_wins([A, _, _, _, A, _, _, _, A]) when A == 'X' orelse A == 'O' -> A; +who_wins([_, _, A, _, A, _, A, _, _]) when A == 'X' orelse A == 'O' -> A; +who_wins(Board) -> + case lists:member('E', Board) of + true -> running; + false -> draw + end. + +move(_, _, Row, _) when Row < 0 orelse Row > 2 -> {bad_arg}; +move(_, _, _, Col) when Col < 0 orelse Col > 2 -> {bad_arg}; +move(_, Move, _, _) when not (Move == 'X') -> {bad_arg}; +move(Board, Move, Row, Col) -> + I = Row * 3 + Col, + Updated = lists:sublist(Board, I) ++ [Move] ++ lists:nthtail(I + 1, Board), + {ok, Updated}. + +format(Board) when length(Board) == 9 -> + B = lists:map(fun(C) -> case C of 'E' -> ' '; _ -> C end end, Board), + io:format("+---+~n|~s~s~s|~n|~s~s~s|~n|~s~s~s|~n+---+~n", B); +format(_) -> "bad board". diff --git a/tictactoe/ttt/test/ttt_test.erl b/tictactoe/ttt/test/ttt_test.erl new file mode 100644 index 0000000..95b6bf7 --- /dev/null +++ b/tictactoe/ttt/test/ttt_test.erl @@ -0,0 +1,18 @@ +-module(ttt_test). + +-include_lib("eunit/include/eunit.hrl"). + +empty_board_test() -> + E = ttt:empty_board(), + ?assertEqual(E, ttt:empty_board()). + +moves_board_test() -> + B0 = ttt:empty_board(), + {ok, B1} = ttt:move(B0, 'X', 0, 0), + {ok, B2} = ttt:move(B1, 'X', 0, 1), + {ok, B3} = ttt:move(B2, 'X', 0, 2), + ?assertEqual(['X', 'X', 'X', 'E', 'E', 'E', 'E', 'E', 'E'], B3). + +simple_test() -> + R = ttt:who_wins(['X', 'X', 'X', 'E', 'E', 'E', 'E', 'E', 'E']), + ?assertEqual('X', R). diff --git a/ttt/rebar.config b/ttt/rebar.config deleted file mode 100644 index 59700d5..0000000 --- a/ttt/rebar.config +++ /dev/null @@ -1,7 +0,0 @@ -{erl_opts, [debug_info]}. -{deps, [ -]}. - -{shell, [ - {apps, [ttt]} -]}. diff --git a/ttt/rebar.lock b/ttt/rebar.lock deleted file mode 100644 index 57afcca..0000000 --- a/ttt/rebar.lock +++ /dev/null @@ -1 +0,0 @@ -[]. diff --git a/ttt/src/ttt.app.src b/ttt/src/ttt.app.src deleted file mode 100644 index 813faba..0000000 --- a/ttt/src/ttt.app.src +++ /dev/null @@ -1,13 +0,0 @@ -{application, ttt, - [{description, "TTT app"}, - {vsn, "0.1.0"}, - {registered, []}, -% {mod, {myapp_app, []}}, - {applications, - [kernel, stdlib]}, - {env,[]}, - {modules, []}, - - {licenses, ["Apache-2.0"]}, - {links, []} - ]}. diff --git a/ttt/src/ttt.erl b/ttt/src/ttt.erl deleted file mode 100644 index a9fc4d1..0000000 --- a/ttt/src/ttt.erl +++ /dev/null @@ -1,51 +0,0 @@ --module(ttt). - --export_type([ - player/0, - square/0, - board/0, - game_result/0]). - --export([ - who_wins/1, - empty_board/0, - move/4, - format/1]). - --type player() :: 'X' | 'O'. --type square() :: player() | 'E'. --type board() :: list(square()). --type game_result() :: player() | 'draw' | 'running'. - --define(EMPTY_BOARD, ['E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E']). - --spec empty_board() -> board(). -empty_board() -> ?EMPTY_BOARD. - --spec who_wins(Board :: board()) -> game_result(). -who_wins([A, A, A, _, _, _, _, _, _]) when A == 'X' orelse A == 'O' -> A; -who_wins([_, _, _, A, A, A, _, _, _]) when A == 'X' orelse A == 'O' -> A; -who_wins([_, _, _, _, _, _, A, A, A]) when A == 'X' orelse A == 'O' -> A; -who_wins([A, _, _, A, _, _, A, _, _]) when A == 'X' orelse A == 'O' -> A; -who_wins([_, A, _, _, A, _, _, A, _]) when A == 'X' orelse A == 'O' -> A; -who_wins([_, _, A, _, _, A, _, _, A]) when A == 'X' orelse A == 'O' -> A; -who_wins([A, _, _, _, A, _, _, _, A]) when A == 'X' orelse A == 'O' -> A; -who_wins([_, _, A, _, A, _, A, _, _]) when A == 'X' orelse A == 'O' -> A; -who_wins(Board) -> - case lists:member('E', Board) of - true -> running; - false -> draw - end. - -move(_, _, Row, _) when Row < 0 orelse Row > 2 -> {bad_arg}; -move(_, _, _, Col) when Col < 0 orelse Col > 2 -> {bad_arg}; -move(_, Move, _, _) when not (Move == 'X') -> {bad_arg}; -move(Board, Move, Row, Col) -> - I = Row * 3 + Col, - Updated = lists:sublist(Board, I) ++ [Move] ++ lists:nthtail(I + 1, Board), - {ok, Updated}. - -format(Board) when length(Board) == 9 -> - B = lists:map(fun(C) -> case C of 'E' -> ' '; _ -> C end end, Board), - io:format("+---+~n|~s~s~s|~n|~s~s~s|~n|~s~s~s|~n+---+~n", B); -format(_) -> "bad board". diff --git a/ttt/test/ttt_test.erl b/ttt/test/ttt_test.erl deleted file mode 100644 index 95b6bf7..0000000 --- a/ttt/test/ttt_test.erl +++ /dev/null @@ -1,18 +0,0 @@ --module(ttt_test). - --include_lib("eunit/include/eunit.hrl"). - -empty_board_test() -> - E = ttt:empty_board(), - ?assertEqual(E, ttt:empty_board()). - -moves_board_test() -> - B0 = ttt:empty_board(), - {ok, B1} = ttt:move(B0, 'X', 0, 0), - {ok, B2} = ttt:move(B1, 'X', 0, 1), - {ok, B3} = ttt:move(B2, 'X', 0, 2), - ?assertEqual(['X', 'X', 'X', 'E', 'E', 'E', 'E', 'E', 'E'], B3). - -simple_test() -> - R = ttt:who_wins(['X', 'X', 'X', 'E', 'E', 'E', 'E', 'E', 'E']), - ?assertEqual('X', R). -- cgit v1.2.3