Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- %%%-------------------------------------------------------------------
- %%% @author Daniel latham
- %%% @copyright (C) 2016, Daniel Latham
- %%% @doc
- %%% Implement a two client version of a text based tic-tac-toe program. To this end develop a module t3
- %%% that contains the following functions:
- %%%
- %%% – newgame() starts a new game node and waits for an opponent.
- %%%
- %%% – playwith(Opponent) connects to another Erlang node identified by Opponent and starts a new game.
- %%% If the Opponent does not exist, a call to playwith results in an error. When the game starts the two clients
- %%% randomly decide who starts the game.
- %%%
- %%% – placetoken(Coordinate) validates that Coordinate is a valid move and places a new token at field Coordinate.
- %%% The move is also communicated to the opponent’s client. Coordinate is coordinate in the form of a1, . . ., c3.
- %%% When a winning position is reached or no more move is possible, the program announces the result on both clients
- %%% and the game terminates. If it is not a client’s turn, a call to placetoken prints an error message.
- %%% If there is no ongoing game, a call to placetoken may result in an error.
- %%%
- %%% – tell(Message) sends a message to the opponent. The message will be printed on the console.
- %%%
- %%% Hint: This site may give you some ideas: http://ninenines.eu/articles/tictactoe/
- %%% Note: If the problem description is unclear, please use the Canvas forum to seek clarification.
- %%% (20 points – extra credit) Extend the two client version so the game can be played in a distributed environment.
- %%%
- %%%TORUN:
- %%% Open two terminals and connect to moat.cis.uab.edu
- %%% You should see two vulcan machines
- %%% Go to the directory where this file is saved
- %%% Type below where YOURNAME and OTHERNAME are consistent
- %%% 1$ erl -sname YOURNAME -setcookie .erlang.cookie
- %%% 2$ erl -sname OTHERNAME -setcookie .erlang.cookie
- %%% In both type the following
- %%% (YOURNAME@vulcanX)> c(t3).
- %%% (OTHERNAME@vulcanY)> c(t3).
- %%% Next, start the game with player one(can be you or other, using you)
- %%% (YOURNAME@vulcanX)> t3:newgame().
- %%% Connect to game from player two
- %%% (OTHERNAME@vulcanY)> t3:playwith(YOURNAME@vulcanX).
- %%%
- %%% @end
- %%% Created : 16. Apr 2016 2:20 PM
- %%%-------------------------------------------------------------------
- -module(t3_v2).
- -author("Daniel Latham").
- %% API
- -export([newgame/0, playwith/1, tell/1, client/0, data/1, placetoken/1]).
- %%% Starts new game for player
- %%% Use this before trying to connect
- newgame() ->
- io:fwrite("Newgame created for Player~n"),
- My_Game_PID = spawn(t3_v2, client, []),
- % Register name of client so that other client can communicate with this process
- % on this node
- register(player, My_Game_PID),
- My_Game_PID.
- %%% Use this to play with another node on the same or other machine on the same network
- %%% Usage: t3:playwith(bob@address).
- playwith(Other_Game_Node)->
- Response = net_adm:ping(Other_Game_Node),
- % Check that response is not a 'pang'
- if
- Response == pang ->
- io:fwrite("Invalid host or username. It is possible that the host or user is "),
- io:fwrite("offline or your secret cookie is incorrectly setup. Please try again.~n");
- true ->
- My_Game_PID = newgame(),
- {player, Other_Game_Node} ! {connect, My_Game_PID}
- end.
- %%% Sends message to other player's node
- tell(Message) ->
- Other = get_player(),
- Other ! {message, Message}.
- %%% Generates client for saving data(board, other player, etc)
- data(OtherPlayer, Board, Piece, Lock)->
- receive
- {get_player, Node} ->
- Node ! {give_player, OtherPlayer},
- data(OtherPlayer, Board, Piece, Lock);
- {get_board, Node} ->
- Node ! {give_board, Board},
- data(OtherPlayer, Board, Piece, Lock);
- {get_piece, Node} ->
- Node ! {give_piece, Piece},
- data(OtherPlayer, Board, Piece, Lock);
- {get_lock, Node} ->
- Node ! {give_lock, Lock},
- data(OtherPlayer, Board, Piece, Lock);
- {set_board, NewBoard} ->
- data(OtherPlayer, NewBoard, Piece, Lock);
- {set_lock, NewLock} ->
- data(OtherPlayer, Board, Piece, NewLock)
- end.
- % Initial state with only other player's node
- data(OtherPlayer) ->
- receive
- {get_player, Node} ->
- Node ! {give_player, OtherPlayer},
- data(OtherPlayer);
- {set_up_board, NewBoard, NewPiece, Lock} ->
- data(OtherPlayer, NewBoard, NewPiece, Lock)
- end.
- %%% Two clients generated that talk to each other, one on both systems
- client() ->
- receive
- % Add Other_Player to our data process, send back our node+client
- {connect, Other_Player} ->
- io:fwrite("Connected to Other Player, ~p~n", [Other_Player]),
- % Spawn new data process
- DataPID = spawn(t3_v2, data, [Other_Player]),
- % Set up global name for data process
- register(data, DataPID),
- Other_Player ! {connected, self()};
- % Connected to Other_Player, add full node+client process to our
- % data process
- {connected, Other_Player} ->
- io:fwrite("Connected to Other Player, ~p~n", [Other_Player]),
- % Spawn new data process
- DataPID = spawn(t3_v2, data, [Other_Player]),
- % Set up global name for data process
- register(data, DataPID),
- % Decide who gets to go first
- % Change seed to something random enough
- random:seed(now()),
- My_Num = random:uniform(),
- Other_Num = random:uniform(),
- if
- My_Num >= Other_Num ->
- io:fwrite("'~p' >= '~p'; You begin game...~n", [My_Num, Other_Num]),
- Other_Player ! {print_whobegins, My_Num, Other_Num},
- generate_game();
- My_Num < Other_Num ->
- io:fwrite("'~p' < '~p'; Other Player begins game...~n", [My_Num, Other_Num]),
- Other_Player ! {print_whobegins, My_Num, Other_Num},
- Other_Player ! {gen_game};
- true ->
- ok
- end;
- % Prints who begins the game for the node that created the new game first
- % Player 2 being the one that connected to Player 1
- {print_whobegins, Other_Num, My_Num} ->
- if
- Other_Num >= My_Num ->
- io:fwrite("'~p' >= '~p'; Other Player begins game...~n", [Other_Num, My_Num]);
- true ->
- io:fwrite("'~p' < '~p'; You begin game...~n", [Other_Num, My_Num])
- end;
- {set_up_board} ->
- % Creates board
- Board = new_board(),
- % Sets piece
- Piece = o,
- % Sets LOCK
- Lock = closed,
- % Grabs data process
- Data = whereis(data),
- % Set our board piece to 'o' and lock set to closed
- Data ! {set_up_board, Board, Piece, Lock},
- io:fwrite("You are 'o'.~n"),
- io:fwrite("Other player is making move..Please wait~n");
- {message, Message} ->
- io:fwrite("Other Player ~p says: ~p~n", [get_player(), Message]);
- {gen_game} ->
- io:fwrite("Generating game...~n~n"),
- generate_game();
- {results, Board, Result} ->
- % 0 for tie, 1 for other wins
- io:fwrite("~n~n$$$$$$$$$$$$FINAL BOARD$$$$$$$$$$$~n~n"),
- print_board(Board),
- if
- Result == 0 ->
- io:fwrite("~nIt's a tie! Thanks for playing!~n");
- true ->
- io:fwrite("~nSorry! You lose, Player '~p'!~n", [get_piece()])
- end;
- {your_turn, Coordinate, NewBoard} ->
- set_board(NewBoard),
- io:fwrite("~n~nOther Player set coordinate '~p'~n~n", [Coordinate]),
- print_board(NewBoard),
- %%% unset lock
- set_lock(open),
- Piece = get_piece(),
- io:fwrite("~nYou are '~p'. Use 'placetoken(a1...c3)' to place token on board.~n", [Piece])
- end,
- client().
- %%% Generates the board, piece, and lock and sends to data process
- generate_game() ->
- % Creates board
- Board = new_board(),
- % Sets piece
- Piece = x,
- % Sets LOCK
- Lock = open,
- % Grabs data process
- Data = whereis(data),
- % Set our board piece to 'x' and lock set to open
- Data ! {set_up_board, Board, Piece, Lock},
- % Send message to other node that they must wait, and that they are 'o'
- Other = get_player(),
- Other ! {set_up_board},
- % Print message with board
- print_board(Board),
- io:fwrite("~nYou are 'x'. Use 'placetoken(a1...c3)' to place token on board.~n").
- %%% Below are numerous getters and setters
- %%% Self explanatory
- get_player() ->
- Data = whereis(data),
- Data ! {get_player, self()},
- receive
- {give_player, OtherPlayer} ->
- OtherPlayer
- end.
- get_board() ->
- Data = whereis(data),
- Data ! {get_board, self()},
- receive
- {give_board, Board} ->
- Board
- end.
- get_piece() ->
- Data = whereis(data),
- Data ! {get_piece, self()},
- receive
- {give_piece, Piece} ->
- Piece
- end.
- get_lock() ->
- Data = whereis(data),
- Data ! {get_lock, self()},
- receive
- {give_lock, Lock} ->
- Lock
- end.
- set_board(Board) ->
- Data = whereis(data),
- Data ! {set_board, Board}.
- set_lock(Lock) ->
- Data = whereis(data),
- Data ! {set_lock, Lock}.
- %%% Possibly the most complicated function, placetoken places a token (a1... c3)
- %%% Onto the board. Locks, already placed pieces, and winning states are assessed here
- placetoken(Coordinate) ->
- Board = get_board(),
- Piece = get_piece(),
- Lock = get_lock(),
- Possible = {a1, a2, a3, b1, b2, b3, c1, c2, c3},
- Is_Poss = lists:member(Coordinate, tuple_to_list(Possible)),
- if
- Lock == closed ->
- io:fwrite("It is not your turn yet. Please wait.~n");
- Is_Poss ->
- io:fwrite("~p chosen.~n", [Coordinate]),
- Free = check_element(Coordinate, Board),
- if
- Free == false ->
- io:fwrite("Please choose a free space~n");
- true ->
- % Switched Y and X for compatability
- {Y, X} = convert_coordinate(Coordinate),
- New_Board = place_element(Piece, X, Y, Board),
- set_board(New_Board),
- % Check board to see if you won
- Won = check(get_board()),
- Other = get_player(),
- if
- {victory, x} == Won ->
- io:fwrite("~n~n$$$$$$$$$$$$FINAL BOARD$$$$$$$$$$$~n~n"),
- print_board(get_board()),
- io:fwrite("~nCongratulations! You won, Player '~p'~n", [get_piece()]),
- Other ! {results, get_board(), 1};
- {victory, o} == Won ->
- io:fwrite("~n~n$$$$$$$$$$$$FINAL BOARD$$$$$$$$$$$~n~n"),
- print_board(get_board()),
- io:fwrite("~nCongratulations! You won, Player '~p'~n", [get_piece()]),
- Other ! {results, get_board(), 1};
- draw == Won ->
- io:fwrite("~n~n$$$$$$$$$$$$FINAL BOARD$$$$$$$$$$$~n~n"),
- print_board(get_board()),
- io:fwrite("~nIt's a tie! Thanks for playing!~n"),
- Other ! {results, get_board(), 1};
- true ->
- print_board(get_board()),
- Other ! {your_turn, Coordinate, get_board()},
- % SET LOCK
- set_lock(closed)
- end,
- ok
- end;
- true ->
- io:fwrite("No correct unit chosen. Correct units include: ~p.~n", [Possible])
- end.
- %%% Converts coordinate atom into {X, Y} numerical tuple
- convert_coordinate(Co) ->
- Co_str = atom_to_list(Co),
- Co_str_0 = string:substr(Co_str, 1, 1),
- Co_str_1 = string:substr(Co_str, 2, 1),
- if
- Co_str_0 == "a" ->
- X = 1;
- Co_str_0 == "b" ->
- X = 2;
- Co_str_0 == "c" ->
- X = 3;
- true ->
- X = -1
- end,
- if
- Co_str_1 == "1" ->
- Y = 1;
- Co_str_1 == "2" ->
- Y = 2;
- Co_str_1 == "3" ->
- Y = 3;
- true ->
- Y = -1
- end,
- {X, Y}.
- %%% prints the board by calling greater function below
- print_board(Board) ->
- print_board(Board, 1).
- %%% Prints the board, recursively going line by line, piece by piece
- print_board(Board, Index) ->
- if
- Index < 10 ->
- L = element(Index, Board);
- true ->
- L = undefined
- end,
- New_Index = Index + 1,
- case Index of
- 1 ->
- io:fwrite("#############~n"),
- io:fwrite("# ~p ", [L]),
- print_board(Board, New_Index);
- 2 ->
- io:fwrite(" ~p ", [L]),
- print_board(Board, New_Index);
- 3 ->
- io:fwrite(" ~p #~n", [L]),
- print_board(Board, New_Index);
- 4 ->
- io:fwrite("# ~p ", [L]),
- print_board(Board, New_Index);
- 5 ->
- io:fwrite(" ~p ", [L]),
- print_board(Board, New_Index);
- 6 ->
- io:fwrite(" ~p #~n", [L]),
- print_board(Board, New_Index);
- 7 ->
- io:fwrite("# ~p ", [L]),
- print_board(Board, New_Index);
- 8 ->
- io:fwrite(" ~p ", [L]),
- print_board(Board, New_Index);
- 9 ->
- io:fwrite(" ~p #~n", [L]),
- io:fwrite("#############~n"),
- print_board(Board, New_Index);
- _ ->
- ok
- end.
- %%% Generates a new board and returns it
- new_board() ->
- {l, l, l,
- l, l, l,
- l, l, l}.
- %%% Places an element on the board based on the X, Y coordinate
- place_element(Who, X, Y, Board) ->
- setelement((Y - 1) * 3 + X, Board, Who).
- %%% Checks the element in question to make sure it is not overwritten
- %%% This is also done with pattern matching
- check_element(Element, Board) ->
- if
- Element == a1 ->
- {Key, _, _,
- _, _, _,
- _, _, _} = Board;
- Element == a2 ->
- {_, Key, _,
- _, _, _,
- _, _, _} = Board;
- Element == a3 ->
- {_, _, Key,
- _, _, _,
- _, _, _} = Board;
- Element == b1 ->
- {_, _, _,
- Key, _, _,
- _, _, _} = Board;
- Element == b2 ->
- {_, _, _,
- _, Key, _,
- _, _, _} = Board;
- Element == b3 ->
- {_, _, _,
- _, _, Key,
- _, _, _} = Board;
- Element == c1 ->
- {_, _, _,
- _, _, _,
- Key, _, _} = Board;
- Element == c2 ->
- {_, _, _,
- _, _, _,
- _, Key, _} = Board;
- Element == c3 ->
- {_, _, _,
- _, _, _,
- _, _, Key} = Board;
- true ->
- io:fwrite("Should never get here~n"),
- Key = undefined
- end,
- if
- Key == x ->
- io:fwrite("Place ~p taken by 'x'~n", [Element]),
- false;
- Key == o ->
- io:fwrite("Place ~p taken by 'o'~n", [Element]),
- false;
- true ->
- io:fwrite("Place ~p is free.~n", [Element]),
- true
- end.
- %%% Checks to see if payer has won the game or a tie
- %%% Basically just matches the board with a prewritten board
- check(Board) ->
- case Board of
- {x, x, x,
- _, _, _,
- _, _, _} -> {victory, x};
- {_, _, _,
- x, x, x,
- _, _, _} -> {victory, x};
- {_, _, _,
- _, _, _,
- x, x, x} -> {victory, x};
- {x, _, _,
- x, _, _,
- x, _, _} -> {victory, x};
- {_, x, _,
- _, x, _,
- _, x, _} -> {victory, x};
- {_, _, x,
- _, _, x,
- _, _, x} -> {victory, x};
- {x, _, _,
- _, x, _,
- _, _, x} -> {victory, x};
- {_, _, x,
- _, x, _,
- x, _, _} -> {victory, x};
- {o, o, o,
- _, _, _,
- _, _, _} -> {victory, o};
- {_, _, _,
- o, o, o,
- _, _, _} -> {victory, o};
- {_, _, _,
- _, _, _,
- o, o, o} -> {victory, o};
- {o, _, _,
- o, _, _,
- o, _, _} -> {victory, o};
- {_, o, _,
- _, o, _,
- _, o, _} -> {victory, o};
- {_, _, o,
- _, _, o,
- _, _, o} -> {victory, o};
- {o, _, _,
- _, o, _,
- _, _, o} -> {victory, o};
- {_, _, o,
- _, o, _,
- o, _, _} -> {victory, o};
- {A, B, C,
- D, E, F,
- G, H, I} when A =/= l, B =/= l, C =/= l,
- D =/= l, E =/= l, F =/= l,
- G =/= l, H =/= l, I =/= l ->
- draw;
- _ -> ok
- end.
Advertisement