% % tttClient.erl % -module(tttClient). -author('Alan G. Labouseur'). -define(else, true). -define(id, "-- client: "). % % Public % -export([start/0, play/0, play/1]). start() -> io:fwrite("~sTTT client started on node ~w (pid ~w) ", [?id, node(), self()]), ClientPid = spawn(fun clientLoop/0), % We want to publish this process in Erlang's process registry. % Before we do that, we need to un-register it if it's already been registered. SomePlace = whereis(tttClient), if (SomePlace /= undefined) -> % "not undefined" = "is defined" (This is horrible, I know.) unregister(tttClient); ?else -> % The proccess was NOT already published/registered, so we don't really want to do anything here. true % This is dumb, but I couldn't think of a better "no-op". end, % Publish this process in Erlang's process registry. register(tttClient, ClientPid), io:fwrite("with pid ~w registered as ~w.~n", [ClientPid, tttClient]). play() -> io:fwrite("You must supply a node.~n", []). play(ServerNode) -> io:fwrite("~sSending [start_game] request to node ~w.~n",[?id, ServerNode]), {tttServer, ServerNode} ! {node(), start_game}. % % Private, but accepting messages sent to clientLoop because of the way it was spawned. % clientLoop() -> receive {FromNode, player_turn, Board} -> io:fwrite("~sReceived [player_turn] request from node ~w with board ~p.~n",[?id, FromNode, Board]), displayBoard(Board), io:fwrite("~s", [?id]), {ok, PlayerMove} = io:fread("Where do you want to move [1-9]? ", "~d"), % PlayerMove gets read as a list. NewBoard = processPlayerMove(hd(PlayerMove), Board), % For debugging -- io:fwrite("~sNew board ~p.~n",[?id, NewBoard]), io:fwrite("~sSending [computer_turn] response to node ~w.~n",[?id, FromNode]), {tttServer, FromNode} ! {node(), computer_turn, NewBoard}, clientLoop(); {FromNode, _Any} -> io:fwrite("~sReceived unknown request [~p] from node ~w.~n",[?id, _Any, FromNode]), clientLoop() end. % % Private; no messages either. % displayBoard(Board) -> io:fwrite("Board: ~p~n", [Board]). processPlayerMove(Position, Board) -> Target = lists:nth(Position, Board), if(Target == 0) -> io:fwrite("~sPlacing an X into position ~w.~n", [?id, Position]), UpdatedBoard = replaceInList(1, Position, Board), UpdatedBoard; ?else -> io:fwrite("~sCannot place an X into position ~w.~n", [?id, Position]), Board end. % if replaceInList(Value, Position, List) -> {Part1, Part2} = lists:split(Position-1, List), % Break the list in two just before the specified Position. [_ | Tail] = Part2, % Separate Part2 into Head and Tail, discarding the Head. Part1 ++ [Value] ++ Tail. % CONS together the result: Part1 ++ the new Value ++ the Tail from Part2. % I wonder, is it really a good idea to process the player's move in the client? % Perhaps not. We should ... % TODO: Move this to the server se we can keep the client as minimal as possible.