%%%-------------------------------------------------------------------
%%% @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.
Comments
0 B
|👍
/👎
0 B
|👍
/👎