# Unit Players of BattleshipGames

by Josh Blake: L6 Age ~17

unit Players; { Copyright (c) 2014 Josh Blake Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License, as described at http://www.apache.org/licenses/ and http://www.pp4s.co.uk/licenses/ } interface uses Game, Grid, SysUtils, Settings, UI; type TPlayer = class protected Game: TGame; //the game we are playing UI: TBaseUI; //the UI being used function SelectGuess: TCoord; virtual; abstract; public function TakeTurn: Integer; constructor Create(aGame: TGame; aUI: TBaseUI); end; AIEasy = class(TPlayer) private FoundShip: TCoord; //position where ship was found CurrentDirection, //represents direction in which we've guessed ship lies TurnsSinceDestroy: //number of guesses on current mode Integer; Remaining: array of TCoord; function DestroyShip: TCoord; function RandomGuess: TCoord; procedure Setup(const GridSize: Integer); protected function SelectGuess: TCoord; override; end; User = class(TPlayer) protected function SelectGuess: TCoord; override; end; implementation constructor TPlayer.Create(aGame: TGame; aUI: TBaseUI); begin Game := aGame; UI := aUI; end; function TPlayer.TakeTurn: Integer; var GuessResult: TGuessResult; Guess: TCoord; begin repeat Guess := SelectGuess; GuessResult := Game.ProcessGuess(Guess); until GuessResult <> Invalid; //a turn has to have a valid guess if (GuessResult = Won) then Result := Game.GuessCount else begin Result := -1; end; end; procedure AIEasy.Setup(const GridSize: Integer); var X, Y, I: Integer; begin //Mark all grid squares as remaining SetLength(Remaining, GridSize * GridSize); I := 0; for X := 0 to GridSize - 1 do begin for Y := 0 to GridSize - 1 do begin Remaining[I].X := X; Remaining[I].Y := Y; Inc(I); end; end; //Mark that no ship has been found FoundShip.X := -1; end; function AIEasy.DestroyShip: TCoord; var BaseGuess: TCoord; //guess to base next move off begin if (CurrentDirection = -1) or (Game.LastGuessResult <> Hit) then begin CurrentDirection := Random(4); //Need a new direction end; if Game.LastGuessResult = Hit then BaseGuess := Game.LastGuess //correct direction, continue searching else BaseGuess := FoundShip; case CurrentDirection of 0: Dec(BaseGuess.Y); //Guess up 1: Inc(BaseGuess.X); //Guess right 2: Inc(BaseGuess.Y); //Guess down 3: Dec(BaseGuess.X); //Guess left end; Result := BaseGuess; end; function AIEasy.RandomGuess: TCoord; var Selection, I: Integer; begin Selection := Random(Length(Remaining)); //Select a random square Result := Remaining[Selection]; //return the selection //Remove from remaining for I := Selection to Length(Remaining) - 2 do begin Remaining[I] := Remaining[I + 1]; end; SetLength(Remaining, Length(Remaining) - 1); end; function AIEasy.SelectGuess: TCoord; begin Log('Select Guess'); if Game.LastGuessResult = None then Setup(Length(Game.Grid)); //setup for new game if (Game.LastGuessResult = Hit) and (FoundShip.X = -1) then begin TurnsSinceDestroy := 0; FoundShip := Game.LastGuess; //look for rest of ship CurrentDirection := -1; end; if Game.LastGuessResult = Sunk then begin FoundShip.X := -1; //ship sunk, don't keep searching TurnsSinceDestroy := 0; end; if FoundShip.X <> -1 then begin Inc(TurnsSinceDestroy); if TurnsSinceDestroy > 31 then begin FoundShip.X := -1; //stop infinite loop as AI is stupid for some situations end; Result := DestroyShip; end else begin Inc(TurnsSinceDestroy); //track turns since last state change Result := RandomGuess; end; end; function User.SelectGuess: TCoord; begin Result := UI.GetUserGuess(Game); end; end.