- Timestamp:
- Dec 13, 2020, 1:52:47 AM (4 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Games/UClovece.pas
r3 r4 25 25 MoveCount: Integer; 26 26 Player: TPlayer; 27 Selectable: Boolean; 27 28 property Tile: TTile read FTile write SetTile; 28 29 end; … … 35 36 function GetSingleByTileKind(Kind: TTileKindSet): TToken; 36 37 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); 38 43 39 44 { TPlayer } … … 46 51 StartTile: TTile; 47 52 TileBeforeHome: TTile; 53 Mode: TPlayerMode; 48 54 function CanPlay: Boolean; 49 55 procedure Reset(TokenCount: Integer); … … 99 105 end; 100 106 101 TGameState = (gsWaiting, gs Running, gsGameOver);107 TGameState = (gsWaiting, gsDiceThrow, gsTokenSelect, gsGameOver); 102 108 103 109 { TClovece } … … 107 113 MetaCanvas: TMetaCanvas; 108 114 Processing: Boolean; 115 SelectedToken: TToken; 116 Zoom: Double; 117 Shift1: TPoint; 118 Shift2: TPoint; 119 TokensMoved: Integer; 109 120 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; 110 125 public 111 126 Board: TBoard; … … 186 201 function TTile.GetNext(Steps: Integer; Player: TPlayer): TTile; 187 202 begin 203 // Self 188 204 Result := nil; 189 205 if Steps > 0 then begin … … 191 207 Result := NextTileHome.GetNext(Steps - 1, Player) 192 208 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; 194 213 end; 195 214 end else Result := Self; … … 210 229 procedure TTile.Paint(Canvas: TCanvas); 211 230 begin 231 Canvas.Pen.Color := clBlack; 212 232 Canvas.Pen.Width := 5; 213 233 if Assigned(Owner) then Canvas.Brush.Color := Owner.Color … … 228 248 Position.Y + TokenSize div 2 229 249 ); 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; 230 260 end; 231 261 if Assigned(AnimateToken) then begin … … 299 329 for I := 0 to Count - 1 do 300 330 if Items[I].Tile.Kind in Kind then Tiles.Add(Items[I]); 331 end; 332 333 procedure TTokens.GetSelectableTokens(const Tiles: TTokens); 334 var 335 I: Integer; 336 begin 337 Tiles.Clear; 338 for I := 0 to Count - 1 do 339 if Items[I].Selectable then Tiles.Add(Items[I]); 340 end; 341 342 function TTokens.GetSelectableTokensCount: Integer; 343 var 344 I: Integer; 345 begin 346 Result := 0; 347 for I := 0 to Count - 1 do 348 if Items[I].Selectable then Inc(Result); 301 349 end; 302 350 … … 500 548 end; 501 549 550 function TClovece.AISelectToken: TToken; 551 var 552 Token: TToken; 553 Tokens: TTokens; 554 SortedTokens: TTokens; 555 begin 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; 577 end; 578 579 procedure TClovece.MoveToken(Token: TToken); 580 var 581 NewTile: TTile; 582 begin 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; 613 end; 614 615 procedure TClovece.NextPlayer(SelectableTokenCount: Integer); 616 begin 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; 628 end; 629 630 function TClovece.GetBoardPos(P: TPoint): TPoint; 631 begin 632 Result := SubPoint(P, Shift2); 633 Result := Point(Trunc(Result.X / Zoom), Trunc(Result.Y / Zoom)); 634 Result := SubPoint(Result, Shift1); 635 end; 636 502 637 procedure TClovece.ThrowDice; 503 638 var 504 639 Token: TToken; 505 640 NewTile: TTile; 506 TurnDone: Boolean;507 641 I: Integer; 508 642 SortedTokens: TTokens; 509 begin 510 TurnDone := False; 511 DiceValue := Random(6) + 1; 512 LastDiceValue := DiceValue; 643 SelectableTokenCount: Integer; 644 begin 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 { 513 690 if DiceValue = 6 then begin 514 691 if CurrentPlayer.Tokens.GetCountByTileKind([tkYard]) > 0 then begin … … 528 705 end; 529 706 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 begin531 // Own token is on start position, move it away532 Token := CurrentPlayer.StartTile.Token;533 NewTile := Token.Tile.GetNext(DiceValue, CurrentPlayer);534 if not Assigned(NewTile.Token) or (NewTile.Token.Player <> CurrentPlayer) then begin535 AnimateTokenMovement(Token, NewTile);536 TurnDone := True;537 end;538 end;539 if not TurnDone then begin540 SortedTokens := TTokens.Create(False);541 try542 CurrentPlayer.Tokens.GetByTileKind([tkNone, tkHome], SortedTokens);543 SortedTokens.Sort(CompareTokenMoveCount);544 for I := 0 to SortedTokens.Count - 1 do begin545 Token := SortedTokens[I];546 NewTile := Token.Tile.GetNext(DiceValue, CurrentPlayer);547 if not Assigned(NewTile) then Continue;548 if Assigned(NewTile.Token) then begin549 if NewTile.Token.Player = Token.Player then begin550 // Another own token is blocking way, use another one if possible551 Continue;552 end else begin553 // Move opponents token back to its yard554 NewTile.Token.Tile := NewTile.Token.Player.YardTiles.GetTileWithoutToken;555 AnimateTokenMovement(Token, NewTile);556 TurnDone := True;557 Break;558 end;559 end else begin560 // Normal token move561 AnimateTokenMovement(Token, NewTile);562 TurnDone := True;563 Break;564 end;565 end;566 finally567 SortedTokens.Free;568 end;569 end;570 707 end; 571 708 Inc(DiceThrow); … … 580 717 end; 581 718 if Players.CanPlayCount < 2 then State := gsGameOver; 719 } 582 720 Repaint; 583 721 end; 584 722 585 723 procedure TClovece.MouseUp(Button: TMouseButton; Position: TPoint); 586 begin 587 if (State = gsRunning) and not Processing then begin 724 var 725 I: Integer; 726 Tile: TTile; 727 BoardPos: TPoint; 728 begin 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 588 739 Processing := True; 589 740 ThrowDice; 590 741 Processing := False; 591 742 end; 743 Repaint; 592 744 end; 593 745 … … 610 762 var 611 763 I: Integer; 764 Player: TPlayer; 612 765 begin 613 766 Players.Clear; 614 767 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; 616 770 end; 617 771 Board.Build(Players.Count, TokenCount); … … 622 776 DiceValue := -1; 623 777 DiceThrow := 0; 624 State := gs Running;778 State := gsDiceThrow; 625 779 Repaint; 626 780 end; … … 642 796 Board.Paint(MetaCanvas); 643 797 R := MetaCanvas.GetRealRect; 644 MetaCanvas.Move(Point(-R.Left, -R.Top)); 798 Shift1 := Point(-R.Left, -R.Top); 799 MetaCanvas.Move(Shift1); 645 800 // Scale to window size 646 801 WidthFactor := Size.Width / R.Width / 1.05; 647 802 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); 650 806 // Center to window 651 807 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); 653 810 MetaCanvas.DrawTo(Canvas); 654 811 -
trunk/UFormMain.pas
r2 r4 114 114 procedure TFormMain.FormCreate(Sender: TObject); 115 115 begin 116 Randomize; 116 117 Game := TClovece.Create; 117 118 GameCanvas := TGameCanvas.Create;
Note:
See TracChangeset
for help on using the changeset viewer.