Skip to content

Commit

Permalink
Added a fancy GUI
Browse files Browse the repository at this point in the history
  • Loading branch information
skotz committed Dec 14, 2016
1 parent fa53385 commit ba22cc1
Show file tree
Hide file tree
Showing 18 changed files with 1,075 additions and 166 deletions.
Binary file added About/screenshot-2016-12-13-a.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added About/screenshot-2016-12-13-b.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions Chase.Engine/Chase.Engine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@
<Compile Include="Direction.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="Game.cs" />
<Compile Include="Interfaces\ISearchAlgorithm.cs" />
<Compile Include="Move.cs" />
<Compile Include="SearchResult.cs" />
<Compile Include="Player.cs" />
<Compile Include="Position.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Search.cs" />
<Compile Include="SearchStatus.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Expand Down
110 changes: 99 additions & 11 deletions Chase.Engine/Game.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using Chase.Engine.Interfaces;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
Expand All @@ -15,19 +17,83 @@ public class Game

private List<Position> History;

public string Status { get; private set; }
private ISearchAlgorithm search;

public delegate void SearchProgress(SearchStatus status);

/// <summary>
/// Raised whenever there's an update to a pending search
/// </summary>
public event SearchProgress OnSearchProgress;

public delegate void FoundBestMove(SearchResult result);

/// <summary>
/// Raised whenever a best move was found
/// </summary>
public event FoundBestMove OnFoundBestMove;

public event EventHandler<Player> OnGameOver;

private BackgroundWorker worker;

private SearchResult bestMove;

public Game()
{
StartNew();

search = new Search();
search.OnNewResult += Search_OnNewResult;

worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += Worker_DoWork;
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
worker.ProgressChanged += Worker_ProgressChanged;
}

public void StartNew()
{
Board = Position.NewPosition();
History = new List<Position>();

History.Add(Board.Clone());
}

private void Search_OnNewResult(object sender, SearchStatus e)
{
worker.ReportProgress(100, e);
}

private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
OnSearchProgress?.Invoke((SearchStatus)e.UserState);
}

private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Notify our subscribers that we found a move
OnFoundBestMove?.Invoke(bestMove);
}

private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
int depth = (int)e.Argument;
bestMove = GetBestMove(depth);
}

public SearchResult GetBestMove(int searchDepth)
{
return Search.GetBestMove(Board, searchDepth);
return search.GetBestMove(Board, searchDepth);
}

public void BeginGetBestMove(int searchDepth)
{
if (!worker.IsBusy)
{
// Start the search in a background thread
worker.RunWorkerAsync(searchDepth);
}
}

public void MakeMove(string move)
Expand All @@ -40,17 +106,40 @@ public void MakeMove(Move move)
{
Board.MakeMove(move);
History.Add(Board.Clone());

// See if the game is over
int eval = Search.EvaluatePosition(Board);
if (eval == Constants.VictoryScore)
Player winner = GetWinner();
if (winner != Player.None)
{
OnGameOver?.Invoke(this, winner);
}
}

public Player GetWinner()
{
int bluePieces = 0;
int redPieces = 0;
for (int i = 0; i < Constants.BoardSize; i++)
{
if (Board[i] > 0)
{
bluePieces++;
}
else if (Board[i] < 0)
{
redPieces++;
}
}

if (bluePieces < Constants.MinimumPieceCount)
{
Status = "Blue Wins!";
return Player.Red;
}
else if (eval == -Constants.VictoryScore)
else if (redPieces < Constants.MinimumPieceCount)
{
Status = "Red Wins!";
return Player.Blue;
}
return Player.None;
}

public List<Move> GetAllMoves()
Expand All @@ -63,7 +152,6 @@ public void SaveGameToFile(string file)
using (StreamWriter w = new StreamWriter(file))
{
w.WriteLine("Moves: " + Board.MovesHistory);
w.WriteLine("Result: " + Status);
w.WriteLine("--------------------------------------");
foreach (Position position in History)
{
Expand Down
15 changes: 15 additions & 0 deletions Chase.Engine/Interfaces/ISearchAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chase.Engine.Interfaces
{
interface ISearchAlgorithm
{
SearchResult GetBestMove(Position position, int searchDepth);

event EventHandler<SearchStatus> OnNewResult;
}
}
3 changes: 2 additions & 1 deletion Chase.Engine/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Chase.Engine
public enum Player
{
Blue,
Red
Red,
None
}
}
10 changes: 3 additions & 7 deletions Chase.Engine/Position.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public class Position
{
public int[] Board { get; private set; }

public int this[int index] { get { return Board[index]; } }

public Player PlayerToMove { get; set; }

public int PointsToDistribute { get; private set; }
Expand All @@ -23,8 +25,6 @@ public Position()
public void MakeMove(string move)
{
// TODO: parse string moves and validate before making the move

// TODO:
}

public void MakeMove(Move move)
Expand All @@ -39,7 +39,7 @@ public void MakeMove(Move move)
Board[move.ToIndex] += Board[move.ToIndex] > 0 ? move.Increment : -move.Increment;
Board[move.FromIndex] -= Board[move.FromIndex] > 0 ? move.Increment : -move.Increment;
}
else if (PointsToDistribute > 0)
else
{
// We're adding points to a die after another one of our dice was captured
Board[move.ToIndex] += Board[move.ToIndex] > 0 ? move.Increment : -move.Increment;
Expand All @@ -50,10 +50,6 @@ public void MakeMove(Move move)
// It's still our turn after adding points to a piece
opponent = PlayerToMove;
}
else
{
throw new Exception("Impossible State?");
}
}
else
{
Expand Down
36 changes: 28 additions & 8 deletions Chase.Engine/Search.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
using System;
using Chase.Engine.Interfaces;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chase.Engine
{
public class Search
public class Search : ISearchAlgorithm
{
private static int evaluations;

public static SearchResult GetBestMove(Position position, int searchDepth)
private static Stopwatch timer;

public event EventHandler<SearchStatus> OnNewResult;

public SearchResult GetBestMove(Position position, int searchDepth)
{
timer = Stopwatch.StartNew();
evaluations = 0;

SearchResult result = AlphaBetaSearch(position, int.MinValue, int.MaxValue, searchDepth * 2);
SearchResult result = AlphaBetaSearch(position, int.MinValue, int.MaxValue, searchDepth * 2, 1);
result.Evaluations = evaluations;

return result;
}

public static int EvaluatePosition(Position position)
private int EvaluatePosition(Position position)
{
// Start with a small amount of randomness to prevent always choosing one of two equal moves
int eval = Constants.Rand.Next(3) - 1;
Expand All @@ -30,7 +37,6 @@ public static int EvaluatePosition(Position position)
int bluePieces = 0;
int redPieces = 0;


evaluations++;

// Material (number of pieces) difference
Expand Down Expand Up @@ -69,7 +75,7 @@ public static int EvaluatePosition(Position position)
return eval;
}

private static SearchResult AlphaBetaSearch(Position position, int alpha, int beta, int depth)
private SearchResult AlphaBetaSearch(Position position, int alpha, int beta, int depth, int reportdepth)
{
// Evaluate the position
int eval = EvaluatePosition(position);
Expand Down Expand Up @@ -101,14 +107,15 @@ private static SearchResult AlphaBetaSearch(Position position, int alpha, int be
};

List<Move> moves = position.GetValidMoves();
int movenum = 1;
foreach (Move move in moves)
{
// Copy the board and make a move
Position copy = position.Clone();
copy.MakeMove(move);

// Find opponents best counter move
SearchResult child = AlphaBetaSearch(copy, alpha, beta, depth - 1);
SearchResult child = AlphaBetaSearch(copy, alpha, beta, depth - 1, reportdepth - 1);

if (maximizingPlayer)
{
Expand Down Expand Up @@ -144,6 +151,19 @@ private static SearchResult AlphaBetaSearch(Position position, int alpha, int be
break;
}
}

if (reportdepth > 0)
{
// Report on the progress of our search
OnNewResult(null, new SearchStatus()
{
BestMoveSoFar = best,
SearchedNodes = evaluations,
ElapsedMilliseconds = timer.ElapsedMilliseconds,
CurrentMove = movenum++,
TotalMoves = moves.Count
});
}
}

return best;
Expand Down
23 changes: 23 additions & 0 deletions Chase.Engine/SearchStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chase.Engine
{
public class SearchStatus
{
public SearchResult BestMoveSoFar { get; set; }

public long SearchedNodes { get; set; }

public long ElapsedMilliseconds { get; set; }

public int TotalMoves { get; set; }

public int CurrentMove { get; set; }

public decimal NodesPerSecond { get { return (SearchedNodes * 100.0M) / (ElapsedMilliseconds / 1000.0M); } }
}
}
17 changes: 16 additions & 1 deletion Chase.GUI/Chase.GUI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Chase.GUI</RootNamespace>
<AssemblyName>Chase.GUI</AssemblyName>
<AssemblyName>Chase</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
Expand All @@ -32,6 +32,9 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
Expand All @@ -54,6 +57,12 @@
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="TestForm.Designer.cs">
<DependentUpon>TestForm.cs</DependentUpon>
</Compile>
<EmbeddedResource Include="GameForm.resx">
<DependentUpon>GameForm.cs</DependentUpon>
</EmbeddedResource>
Expand All @@ -66,6 +75,9 @@
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<EmbeddedResource Include="TestForm.resx">
<DependentUpon>TestForm.cs</DependentUpon>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
Expand All @@ -85,6 +97,9 @@
<Name>Chase.Engine</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="icon.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
Loading

0 comments on commit ba22cc1

Please sign in to comment.