Changeset 4


Ignore:
Timestamp:
Dec 13, 2020, 1:52:47 AM (3 years ago)
Author:
chronos
Message:
  • Added: Human and AI player mode.
  • Added: Human can select one of offered tokens for movement.
Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Games/UClovece.pas

    r3 r4  
    2525    MoveCount: Integer;
    2626    Player: TPlayer;
     27    Selectable: Boolean;
    2728    property Tile: TTile read FTile write SetTile;
    2829  end;
     
    3536    function GetSingleByTileKind(Kind: TTileKindSet): TToken;
    3637    procedure GetByTileKind(Kind: TTileKindSet; const Tiles: TTokens);
    37   end;
     38    procedure GetSelectableTokens(const Tiles: TTokens);
     39    function GetSelectableTokensCount: Integer;
     40  end;
     41
     42  TPlayerMode = (pmHuman, pmAI);
    3843
    3944  { TPlayer }
     
    4651    StartTile: TTile;
    4752    TileBeforeHome: TTile;
     53    Mode: TPlayerMode;
    4854    function CanPlay: Boolean;
    4955    procedure Reset(TokenCount: Integer);
     
    99105  end;
    100106
    101   TGameState = (gsWaiting, gsRunning, gsGameOver);
     107  TGameState = (gsWaiting, gsDiceThrow, gsTokenSelect, gsGameOver);
    102108
    103109  { TClovece }
     
    107113    MetaCanvas: TMetaCanvas;
    108114    Processing: Boolean;
     115    SelectedToken: TToken;
     116    Zoom: Double;
     117    Shift1: TPoint;
     118    Shift2: TPoint;
     119    TokensMoved: Integer;
    109120    procedure AnimateTokenMovement(Token: TToken; NewTile: TTile);
     121    function AISelectToken: TToken;
     122    procedure MoveToken(Token: TToken);
     123    procedure NextPlayer(SelectableTokenCount: Integer);
     124    function GetBoardPos(P: TPoint): TPoint;
    110125  public
    111126    Board: TBoard;
     
    186201function TTile.GetNext(Steps: Integer; Player: TPlayer): TTile;
    187202begin
     203  // Self
    188204  Result := nil;
    189205  if Steps > 0 then begin
     
    191207      Result := NextTileHome.GetNext(Steps - 1, Player)
    192208    end else begin
    193       if Assigned(NextTile) then Result := NextTile.GetNext(Steps - 1, Player);
     209      if Assigned(NextTile) then Result := NextTile.GetNext(Steps - 1, Player)
     210      else begin
     211        Result := nil;
     212      end;
    194213    end;
    195214  end else Result := Self;
     
    210229procedure TTile.Paint(Canvas: TCanvas);
    211230begin
     231  Canvas.Pen.Color := clBlack;
    212232  Canvas.Pen.Width := 5;
    213233  if Assigned(Owner) then Canvas.Brush.Color := Owner.Color
     
    228248      Position.Y + TokenSize div 2
    229249    );
     250    if Token.Selectable then begin
     251      Canvas.Pen.Color := clBlack;
     252      Canvas.Ellipse(
     253        Position.X - TokenSize div 2 + 10,
     254        Position.Y - TokenSize div 2 + 10,
     255        Position.X + TokenSize div 2 - 10,
     256        Position.Y + TokenSize div 2 - 10
     257      );
     258      Canvas.Pen.Color := clBlack;
     259    end;
    230260  end;
    231261  if Assigned(AnimateToken) then begin
     
    299329  for I := 0 to Count - 1 do
    300330    if Items[I].Tile.Kind in Kind then Tiles.Add(Items[I]);
     331end;
     332
     333procedure TTokens.GetSelectableTokens(const Tiles: TTokens);
     334var
     335  I: Integer;
     336begin
     337  Tiles.Clear;
     338  for I := 0 to Count - 1 do
     339    if Items[I].Selectable then Tiles.Add(Items[I]);
     340end;
     341
     342function TTokens.GetSelectableTokensCount: Integer;
     343var
     344  I: Integer;
     345begin
     346  Result := 0;
     347  for I := 0 to Count - 1 do
     348    if Items[I].Selectable then Inc(Result);
    301349end;
    302350
     
    500548end;
    501549
     550function TClovece.AISelectToken: TToken;
     551var
     552  Token: TToken;
     553  Tokens: TTokens;
     554  SortedTokens: TTokens;
     555begin
     556  Tokens := TTokens.Create(False);
     557  CurrentPlayer.Tokens.GetSelectableTokens(Tokens);
     558  if Tokens.Count = 0 then begin
     559  end else
     560  if Tokens.Count = 1 then begin
     561    Result := Tokens[0];
     562  end else begin
     563    if Tokens.GetCountByTileKind([tkYard]) > 0 then begin
     564      Result := Tokens.GetSingleByTileKind([tkYard]);
     565    end else
     566    if Assigned(CurrentPlayer.StartTile.Token) and (CurrentPlayer.StartTile.Token.Player = CurrentPlayer) then begin
     567      Result := CurrentPlayer.StartTile.Token
     568    end else begin
     569      SortedTokens := TTokens.Create(False);
     570      Tokens.GetByTileKind([tkNone, tkHome], SortedTokens);
     571      Tokens.Sort(CompareTokenMoveCount);
     572      Result := Tokens[0];
     573      SortedTokens.Free;
     574    end;
     575    Tokens.Free;
     576  end;
     577end;
     578
     579procedure TClovece.MoveToken(Token: TToken);
     580var
     581  NewTile: TTile;
     582begin
     583  if Token.Tile.Kind = tkYard then begin
     584    // Move from yard
     585    if (not Assigned(CurrentPlayer.StartTile.Token)) or (
     586    Assigned(CurrentPlayer.StartTile.Token) and (CurrentPlayer.StartTile.Token.Player <> CurrentPlayer)) then begin
     587      if Assigned(CurrentPlayer.StartTile.Token) and (CurrentPlayer.StartTile.Token.Player <> CurrentPlayer) then begin
     588        // Move opponents token back to its yard
     589        CurrentPlayer.StartTile.Token.Tile := CurrentPlayer.StartTile.Token.Player.YardTiles.GetTileWithoutToken;
     590      end;
     591      // Move one token from yard to start
     592      Token.MoveCount := 0;
     593      CurrentPlayer.StartTile.Token := Token;
     594    end;
     595  end else
     596  if Token.Tile.Kind in [tkNone, tkHome] then begin
     597    NewTile := Token.Tile.GetNext(DiceValue, CurrentPlayer);
     598    if Assigned(NewTile) then begin
     599      if Assigned(NewTile.Token) then begin
     600        if NewTile.Token.Player = Token.Player then begin
     601          // Another own token is blocking way, use another one if possible
     602        end else begin
     603          // Move opponents token back to its yard
     604          NewTile.Token.Tile := NewTile.Token.Player.YardTiles.GetTileWithoutToken;
     605          AnimateTokenMovement(Token, NewTile);
     606        end;
     607      end else begin
     608        // Normal token move
     609        AnimateTokenMovement(Token, NewTile);
     610      end;
     611    end;
     612  end;
     613end;
     614
     615procedure TClovece.NextPlayer(SelectableTokenCount: Integer);
     616begin
     617  if ((SelectableTokenCount = 0) and (DiceThrow >= 3)) or
     618    ((SelectableTokenCount = 0) and (TokensMoved > 0)) or
     619    ((SelectableTokenCount > 0) and (DiceValue <> 6)) or
     620    (CurrentPlayer.Tokens.GetCountByTileKind([tkHome]) = TokenCount) then begin
     621    repeat
     622      CurrentPlayer := Players[(Players.IndexOf(CurrentPlayer) + 1) mod Players.Count];
     623    until CurrentPlayer.CanPlay or (Players.CanPlayCount >= 2);
     624    DiceThrow := 0;
     625    DiceValue := -1;
     626    TokensMoved := 0;
     627  end;
     628end;
     629
     630function TClovece.GetBoardPos(P: TPoint): TPoint;
     631begin
     632  Result := SubPoint(P, Shift2);
     633  Result := Point(Trunc(Result.X / Zoom), Trunc(Result.Y / Zoom));
     634  Result := SubPoint(Result, Shift1);
     635end;
     636
    502637procedure TClovece.ThrowDice;
    503638var
    504639  Token: TToken;
    505640  NewTile: TTile;
    506   TurnDone: Boolean;
    507641  I: Integer;
    508642  SortedTokens: TTokens;
    509 begin
    510   TurnDone := False;
    511   DiceValue := Random(6) + 1;
    512   LastDiceValue := DiceValue;
     643  SelectableTokenCount: Integer;
     644begin
     645  if State = gsTokenSelect then begin
     646    if Assigned(SelectedToken) then begin
     647      SelectableTokenCount := CurrentPlayer.Tokens.GetSelectableTokensCount;
     648      for I := 0 to CurrentPlayer.Tokens.Count - 1 do
     649        CurrentPlayer.Tokens[I].Selectable := False;
     650      MoveToken(SelectedToken);
     651      State := gsDiceThrow;
     652      Inc(TokensMoved);
     653      NextPlayer(SelectableTokenCount);
     654      SelectedToken := nil;
     655    end;
     656  end else
     657  if State = gsDiceThrow then begin
     658    DiceValue := Random(6) + 1;
     659    LastDiceValue := DiceValue;
     660    Inc(DiceThrow);
     661
     662    // Select possible tokens for move
     663    SelectableTokenCount := 0;
     664    for I := 0 to CurrentPlayer.Tokens.Count - 1 do begin
     665      Token := CurrentPlayer.Tokens[I];
     666      Token.Selectable := False;
     667      if (DiceValue = 6) and (Token.Tile.Kind = tkYard) and (
     668        not Assigned(CurrentPlayer.StartTile.Token) or
     669        (Assigned(CurrentPlayer.StartTile.Token) and
     670        (CurrentPlayer.StartTile.Token.Player <> CurrentPlayer))) then Token.Selectable := True;
     671      if Token.Tile.Kind in [tkNone, tkHome] then begin
     672        NewTile := Token.Tile.GetNext(DiceValue, CurrentPlayer);
     673        if Assigned(NewTile) and
     674          ((Assigned(NewTile.Token) and (NewTile.Token.Player <> CurrentPlayer)) or
     675          not Assigned(NewTile.Token)) then Token.Selectable := True;
     676      end;
     677      if Token.Selectable then Inc(SelectableTokenCount);
     678    end;
     679    if SelectableTokenCount = 0 then begin
     680      NextPlayer(SelectableTokenCount);
     681    end else begin
     682      State := gsTokenSelect;
     683      if CurrentPlayer.Mode = pmAI then begin
     684        SelectedToken := AISelectToken;
     685        ThrowDice;
     686      end;
     687    end;
     688  end;
     689{
    513690  if DiceValue = 6 then begin
    514691    if CurrentPlayer.Tokens.GetCountByTileKind([tkYard]) > 0 then begin
     
    528705  end;
    529706  if not TurnDone and (CurrentPlayer.Tokens.GetCountByTileKind([tkNone, tkHome]) > 0) then begin
    530     if Assigned(CurrentPlayer.StartTile.Token) and (CurrentPlayer.StartTile.Token.Player = CurrentPlayer) then begin
    531       // Own token is on start position, move it away
    532       Token := CurrentPlayer.StartTile.Token;
    533       NewTile := Token.Tile.GetNext(DiceValue, CurrentPlayer);
    534       if not Assigned(NewTile.Token) or (NewTile.Token.Player <> CurrentPlayer) then begin
    535         AnimateTokenMovement(Token, NewTile);
    536         TurnDone := True;
    537       end;
    538     end;
    539     if not TurnDone then begin
    540       SortedTokens := TTokens.Create(False);
    541       try
    542         CurrentPlayer.Tokens.GetByTileKind([tkNone, tkHome], SortedTokens);
    543         SortedTokens.Sort(CompareTokenMoveCount);
    544         for I := 0 to SortedTokens.Count - 1 do begin
    545           Token := SortedTokens[I];
    546           NewTile := Token.Tile.GetNext(DiceValue, CurrentPlayer);
    547           if not Assigned(NewTile) then Continue;
    548           if Assigned(NewTile.Token) then begin
    549             if NewTile.Token.Player = Token.Player then begin
    550               // Another own token is blocking way, use another one if possible
    551               Continue;
    552             end else begin
    553               // Move opponents token back to its yard
    554               NewTile.Token.Tile := NewTile.Token.Player.YardTiles.GetTileWithoutToken;
    555               AnimateTokenMovement(Token, NewTile);
    556               TurnDone := True;
    557               Break;
    558             end;
    559           end else begin
    560             // Normal token move
    561             AnimateTokenMovement(Token, NewTile);
    562             TurnDone := True;
    563             Break;
    564           end;
    565         end;
    566       finally
    567         SortedTokens.Free;
    568       end;
    569     end;
    570707  end;
    571708  Inc(DiceThrow);
     
    580717  end;
    581718  if Players.CanPlayCount < 2 then State := gsGameOver;
     719  }
    582720  Repaint;
    583721end;
    584722
    585723procedure TClovece.MouseUp(Button: TMouseButton; Position: TPoint);
    586 begin
    587   if (State = gsRunning) and not Processing then begin
     724var
     725  I: Integer;
     726  Tile: TTile;
     727  BoardPos: TPoint;
     728begin
     729  BoardPos := GetBoardPos(Position);
     730  for I := 0 to Board.Tiles.Count - 1 do begin
     731    Tile := Board.Tiles[I];
     732    if Assigned(Tile.Token) and Tile.Token.Selectable and
     733      (Distance(Tile.Position, BoardPos) < (TileSize div 2)) then begin
     734      SelectedToken := Board.Tiles[I].Token;
     735      Break;
     736    end;
     737  end;
     738  if (State in [gsDiceThrow, gsTokenSelect]) and not Processing then begin
    588739    Processing := True;
    589740    ThrowDice;
    590741    Processing := False;
    591742  end;
     743  Repaint;
    592744end;
    593745
     
    610762var
    611763  I: Integer;
     764  Player: TPlayer;
    612765begin
    613766  Players.Clear;
    614767  for I := 0 to PlayerCount - 1 do begin
    615     Players.AddNew('Player ' + IntToStr(I + 1), PlayerColors[I]);
     768    Player := Players.AddNew('Player ' + IntToStr(I + 1), PlayerColors[I]);
     769    //if I > 0 then Player.Mode := pmAI else Player.Mode := pmHuman;
    616770  end;
    617771  Board.Build(Players.Count, TokenCount);
     
    622776  DiceValue := -1;
    623777  DiceThrow := 0;
    624   State := gsRunning;
     778  State := gsDiceThrow;
    625779  Repaint;
    626780end;
     
    642796    Board.Paint(MetaCanvas);
    643797    R := MetaCanvas.GetRealRect;
    644     MetaCanvas.Move(Point(-R.Left, -R.Top));
     798    Shift1 := Point(-R.Left, -R.Top);
     799    MetaCanvas.Move(Shift1);
    645800    // Scale to window size
    646801    WidthFactor := Size.Width / R.Width / 1.05;
    647802    HeightFactor := Size.Height / R.Height / 1.05;
    648     if WidthFactor > HeightFactor then MetaCanvas.Zoom(HeightFactor)
    649       else MetaCanvas.Zoom(WidthFactor);
     803    if WidthFactor > HeightFactor then Zoom := HeightFactor
     804      else Zoom := WidthFactor;
     805    MetaCanvas.Zoom(Zoom);
    650806    // Center to window
    651807    R := MetaCanvas.GetRealRect;
    652     MetaCanvas.Move(Point((Size.Width - R.Width) div 2, (Size.Height - R.Height) div 2));
     808    Shift2 := Point((Size.Width - R.Width) div 2, (Size.Height - R.Height) div 2);
     809    MetaCanvas.Move(Shift2);
    653810    MetaCanvas.DrawTo(Canvas);
    654811
  • trunk/UFormMain.pas

    r2 r4  
    114114procedure TFormMain.FormCreate(Sender: TObject);
    115115begin
     116  Randomize;
    116117  Game := TClovece.Create;
    117118  GameCanvas := TGameCanvas.Create;
Note: See TracChangeset for help on using the changeset viewer.