isefire icon

t3_v2

isefire | PRO | 04/25/16 03:16:37 AM UTC | 0 ⭐ | 10159 👁️ | Never ⏰ | []
Erlang |

15.64 KB

|

None

|

0 👍

/

0 👎

%%%-------------------------------------------------------------------
%%% @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

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎