Skip to content

Commit

Permalink
updated decision engine to calculate 2 layers deep
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrunaugh committed Feb 9, 2024
1 parent fca425e commit 958e192
Show file tree
Hide file tree
Showing 13 changed files with 128 additions and 126 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -560,3 +560,4 @@ FodyWeavers.xsd

# training data
*train_data.csv
*train_data.txt
16 changes: 8 additions & 8 deletions ChessNotationConverter/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using ChessNotationConverter.Models;

const string filePath = "C:\\Users\\bruna\\Downloads\\all_with_filtered_anotations_since1998.txt\\all_with_filtered_anotations_since1998.txt";
const string outputFilePath = "../../../../train_data.csv";
const string outputFilePath = "../../../../train_data.txt";
string line;
int lineNumber = 0;
var games = new List<Game>();
Expand Down Expand Up @@ -45,11 +45,11 @@
// loop through 1000 games
foreach (var game in games)
{
if (game.WhiteElo < 2000 && game.BlackElo < 2000)
{
gameCount--;
continue;
}
//if (game.WhiteElo < 2000 && game.BlackElo < 2000)
//{
// gameCount--;
// continue;
//}

// loop through all positions
for (var i = 1; i < game.Positions.Count; i++)
Expand Down Expand Up @@ -83,8 +83,8 @@
evaluations.Clear();
}

// train on only 100,000 games instead of a million
if (gameCount >= 100000)
// convert/evaluate all 3.5 games
if (gameCount >= 3500000)
break;
}
}
Expand Down
26 changes: 26 additions & 0 deletions DecisionEngine/src/Helpers/BoardHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,31 @@ public static bool AreValidCoords(int x, int y)
}
return false;
}

public static int[] ConvertBoardMatrixToArray(int[,] board)
{
var result = new List<int>();
foreach (var value in board)
{
result.Add(value);
}
return result.ToArray();
}

public static int[,] ConvertArrayToBoardMatrix(int[] intArray)
{
if(intArray.Length != 64)
throw new ArgumentException(nameof(intArray));

var result = GenerateFreshBoard();
for (var i = 0; i <8; i++)
{
for (var j = 0; j < 8; j++)
{
result[i, j] = intArray[(i * 8) + j];
}
}
return result;
}
}
}
8 changes: 8 additions & 0 deletions DecisionEngine/src/Models/EvaluatedMove.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace DecisionEngine.Models
{
public class EvaluatedMove
{
public int[] Position { get; set; }
public float Evaluation { get; set; }
}
}
20 changes: 20 additions & 0 deletions DecisionEngine/src/Models/FutureCalculation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace DecisionEngine.Models
{
public class FutureCalculation
{
public int[] Position { get; set; }
public List<EvaluatedMove> NextMoves { get; set; }
internal (float, float) MinAndMaxEvals()
{
if (NextMoves == null || NextMoves.Count < 1)
{
throw new ArgumentException(nameof(NextMoves));
}

var minValue = NextMoves.Min(nm => nm.Evaluation);
var maxValue = NextMoves.Max(nm => nm.Evaluation);

return (minValue, maxValue);
}
}
}
67 changes: 41 additions & 26 deletions DecisionEngine/src/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using DecisionEngine.Helpers;
using DecisionEngine.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;
using System.Text;
using System.Text.Json;

namespace DecisionEngine;

Expand Down Expand Up @@ -40,8 +42,32 @@ static void Main(string[] args)

var legalMoves = FindAllLegalMoves(currentPosition, player, true);

var nnInputFilePath = "C:/Users/bruna/source/repos/AiChessEngine/neuralnetwork/input.csv";
var nnOutputFilePath = "C:/Users/bruna/source/repos/AiChessEngine/neuralnetwork/output.txt";
var preCalculations = new List<FutureCalculation>();
var enemy = player == Player.White ? Player.Black : Player.White;
foreach(var move in legalMoves)
{
var x = new FutureCalculation()
{
Position = BoardHelper.ConvertBoardMatrixToArray(move),
NextMoves = new List<EvaluatedMove>()
};

var secondLayer = FindAllLegalMoves(move, enemy, true);
foreach(var nextMove in secondLayer)
{
var y = new EvaluatedMove()
{
Position = BoardHelper.ConvertBoardMatrixToArray(nextMove),
Evaluation = 0f
};
x.NextMoves.Add(y);
}

preCalculations.Add(x);
}

var nnInputFilePath = "C:/Users/bruna/source/repos/AiChessEngine/neuralnetwork/input.json";
var nnOutputFilePath = "C:/Users/bruna/source/repos/AiChessEngine/neuralnetwork/output.json";

if (File.Exists(nnInputFilePath))
File.Delete(nnInputFilePath);
Expand All @@ -51,18 +77,8 @@ static void Main(string[] args)

using (var sw = new StreamWriter(nnInputFilePath, true))
{
foreach (var move in legalMoves)
{
try
{
var serializedPosition = SerializePosition(move);
sw.WriteLine(serializedPosition);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
var serializedPayload = JsonSerializer.Serialize(preCalculations);
sw.WriteLine(serializedPayload);
}

ProcessStartInfo start = new ProcessStartInfo();
Expand All @@ -80,21 +96,20 @@ static void Main(string[] args)
if (!File.Exists(nnOutputFilePath))
throw new ApplicationException("python script didn't generate any output file");

var outputText = File.ReadAllText(nnOutputFilePath).TrimEnd().Split("\r\n");

var indexedEvaluations = new List<float>();
foreach(var line in outputText)
{
indexedEvaluations.Add(float.Parse(line));
}
var outputText = File.ReadAllText(nnOutputFilePath);
var computedCalculations = JsonSerializer.Deserialize<List<FutureCalculation>>(outputText);

Assert.AreEqual(legalMoves.Count, indexedEvaluations.Count);
// best option for white is the move that results in a set of moves for black where the lowest of
// that set is scored as high as possible. Highest of the lowest possibilities.
var bestMoveForWhite = computedCalculations.OrderByDescending(cc => cc.MinAndMaxEvals().Item1).First();
var bestMoveForBlack = computedCalculations.OrderBy(cc => cc.MinAndMaxEvals().Item2).First();

var bestMoveIndex = MoveHelper.PickRandomBestEvaluationIndex(indexedEvaluations, player);
var moveName = MoveHelper.GenerateMoveName(currentPosition, legalMoves[bestMoveIndex], player);
var chosenPosition = player == Player.White
? BoardHelper.ConvertArrayToBoardMatrix(bestMoveForWhite.Position)
: BoardHelper.ConvertArrayToBoardMatrix(bestMoveForBlack.Position);

Console.WriteLine(moveName + ":");
Console.WriteLine(SerializePosition(legalMoves[bestMoveIndex], true));
Console.WriteLine(MoveHelper.GenerateMoveName(currentPosition, chosenPosition, Player.White));
Console.WriteLine(SerializePosition(chosenPosition, true));
}

public static List<int[,]> FindAllLegalMoves(int[,] board, Player player, bool includeCheckCheck = false)
Expand Down
30 changes: 18 additions & 12 deletions neuralnetwork/forward.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import os
import json
from keras.models import load_model
import numpy as np

# Load the model
model = load_model('./sbrunaugh_chess_model_v3.keras')
model = load_model('./sbrunaugh_chess_model_v3-lite.keras')

inputFilePath = './input.csv'
outputFilePath = './output.txt'
inputFilePath = './input.json'
outputFilePath = './output.json'

if os.path.exists(outputFilePath):
os.remove(outputFilePath)

# Open the input file and output file
with open(inputFilePath, 'r') as infile, open(outputFilePath, 'w') as outfile:
for line in infile:
input_data = np.array(line.strip().split(','), dtype=int).reshape(1, -1)
data = []

# Run the forward pass
prediction = model.predict(input_data)
with open(inputFilePath, 'r') as inputJson:
data = json.load(inputJson)
for futureCalc in data:
for nextMove in futureCalc['NextMoves']:
model_input = np.array(nextMove['Position'], dtype=int).reshape(1, -1)

# Write the prediction to the output file
outfile.write(f'{prediction[0][0]}\n')
# Run the forward pass
eval = model.predict(model_input)
nextMove['Evaluation'] = float(eval[0][0])
print(nextMove['Evaluation'])

print("Index-specific evaluations saved to ", outputFilePath)
with open(outputFilePath, 'w') as outputJson:
json.dump(data, outputJson)

print("JSON data has been written to ", outputFilePath)
38 changes: 0 additions & 38 deletions neuralnetwork/input.csv

This file was deleted.

1 change: 1 addition & 0 deletions neuralnetwork/input.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions neuralnetwork/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
model.compile(optimizer='rmsprop', loss='mean_squared_error') # Adjusted for regression problem

chunksize = 100000 # Adjust based on your system's memory
total_rows = 8000000
total_rows = 800000
print(f"Total of {total_rows} in training data.")
num_chunks = total_rows // chunksize + 1 # Total number of chunks

# Define the number of splits for the KFold cross-validation
n_splits = 5
n_splits = 3
kf = KFold(n_splits=n_splits)

counter = 1
for chunk in pd.read_csv('../train_data.csv', chunksize=chunksize):
for chunk in pd.read_csv('../train_data.txt', chunksize=chunksize):
# Separate features and labels
X = chunk.iloc[:, :-1] # All columns except the last
y = chunk.iloc[:, -1] # Only the last column
Expand All @@ -43,4 +43,4 @@
print(f"Finished cross-validation on chunk {counter} of {num_chunks}.")
counter += 1

model.save('sbrunaugh_chess_model_v3.keras') # Saves model to disk
model.save('sbrunaugh_chess_model_v3-lite.keras') # Saves model to disk
1 change: 1 addition & 0 deletions neuralnetwork/output.json

Large diffs are not rendered by default.

38 changes: 0 additions & 38 deletions neuralnetwork/output.txt

This file was deleted.

Binary file not shown.

0 comments on commit 958e192

Please sign in to comment.