Permalink
Browse files

Added NumberGuessFitness (work in progress).

Moved Brainf-ck version to App.config so extended type 3 can be optionally selected.
  • Loading branch information...
primaryobjects committed Feb 22, 2013
1 parent 45e1ab7 commit fd56880fea0d052c42e954a6567bf98e4f675fc3
@@ -45,6 +45,7 @@
<Compile Include="Concrete\Research\AddToCharFitness.cs" />
<Compile Include="Concrete\IfThenFitness.cs" />
<Compile Include="Concrete\Research\FibFitness.cs" />
+ <Compile Include="Concrete\Research\NumberGuessFitness.cs" />
<Compile Include="Concrete\ReverseStringFitness.cs" />
<Compile Include="Concrete\HelloUserFitness.cs" />
<Compile Include="Concrete\Research\MultiplyFitness.cs" />
@@ -13,7 +13,7 @@ namespace AIProgrammer.Fitness.Concrete
/// If/Then example. Accepts input from the user (1, 2, 3) and prints out text, depending on the option selected.
/// This fitness encourages diversity by looking at the number of memory registers used and difference in output. This allows the printed statements to evolve independently of each other.
/// Note, input is taken in byte value (not ASCII character).
- /// Recommend a genome size of 650 for 3 character strings.
+ /// Recommend a genome size of 650 for 3 character strings. If using Brainf-ck Extended Type 3 (via CommonManager.cs), a smaller genome size may be used.
/// </summary>
public class IfThenFitness : FitnessBase
{
@@ -0,0 +1,226 @@
+using AIProgrammer.Fitness.Base;
+using AIProgrammer.GeneticAlgorithm;
+using AIProgrammer.Managers;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AIProgrammer.Fitness.Concrete
+{
+ /// <summary>
+ /// Number guessing game. Input a number from 1-10 and the program outputs 'L' for too low or 'H' for too high.
+ /// Was unsuccessful getting the program to learn less than/greater than with just a few numbers.
+ /// Had success with providing every digit from 1-10 during training. Although the GA is learning to print based on a sequence, not { } and not the digit.
+ /// </summary>
+ public class NumberGuessFitness : FitnessBase
+ {
+ private int _trainingCount;
+ private const int _trainingLength = 5;
+
+ public NumberGuessFitness(GA ga, int maxIterationCount, int maxTrainingCount = 1)
+ : base(ga, maxIterationCount)
+ {
+ _trainingCount = maxTrainingCount;
+ if (_targetFitness == 0)
+ {
+ for (int i = 0; i < maxTrainingCount; i++)
+ {
+ _targetFitness += _trainingLength * 256;
+ }
+
+ //_targetFitness += 7 * 256; // ou Win! (minus the Y which is already accounted for in the above loop).
+ }
+ }
+
+ #region FitnessBase Members
+
+ protected override double GetFitnessMethod(string program)
+ {
+ byte input1 = 0;
+ string output1 = "";
+ double countBonus = 0;
+ double penalty = 0;
+ double lengthBonus = 0;
+ HashSet<int> memoryHash = new HashSet<int>();
+ HashSet<int> printCommandHash = new HashSet<int>();
+ byte[,] guesses = new byte[,]
+ {
+ { 1, 2, 3, 4, 5 },
+ { 5, 2, 4, 1, 3 },
+ { 2, 5, 1, 3, 4 }
+ };
+
+ for (int i = 0; i < _trainingCount; i++)
+ {
+ int state = 0;
+ int round = 0;
+ memoryHash.Clear();
+ printCommandHash.Clear();
+
+ try
+ {
+ // Run the program.
+ _bf = new Interpreter(program, () =>
+ {
+ if (state == 0)
+ {
+ // Send input.
+ if (round < _trainingLength)
+ {
+ input1 = guesses[i, round++];
+
+ // Ready for output.
+ state = 1;
+
+ // Clear the console for the next round of output.
+ _console.Clear();
+
+ return input1;
+ }
+ else
+ {
+ // No more input.
+ return 0;
+ }
+ }
+ else
+ {
+ // Not ready for input.
+ return 255;
+ }
+ },
+ (b) =>
+ {
+ _console.Append((char)b);
+
+ // Record the memory register being used for this output. Used to support diversity.
+ if (state == 1)
+ {
+ // Record the instruction index being used for this print statement.
+ if (!printCommandHash.Add(_bf.m_CurrentInstructionPointer))
+ {
+ // This is kind of cheating, but we need to force diversity by decoupling the cases. Force them to use unique print statements, not used by any other case.
+ penalty += 200;
+ }
+
+ // This is a valid output character to consider. Record the memory register of where its data is stored.
+ memoryHash.Add(_bf.m_CurrentDataPointer);
+
+ _output.Append((char)b);
+
+ if (input1 < 3)
+ {
+ output1 = "L";
+ }
+ else if (input1 > 3)
+ {
+ output1 = "H";
+ }
+ else
+ {
+ output1 = "W";
+ }
+
+ // Order bonus.
+ if (_console.Length >= output1.Length)
+ {
+ for (int j = 0; j < output1.Length; j++)
+ {
+ Fitness += 256 - Math.Abs(_console[j] - output1[j]);
+ }
+ }
+
+ // Length bonus (percentage of 100).
+ lengthBonus += 200 * ((output1.Length - Math.Abs(_console.Length - output1.Length)) / output1.Length);
+
+ if (_console.Length == output1.Length)
+ {
+ // Ready for input.
+ state = 0;
+ _output.Append(',');
+ }
+ }
+ else if (state == 0)
+ {
+ // Not ready for output.
+ //penalty += 10;
+ }
+ });
+ _bf.Run(_maxIterationCount);
+ }
+ catch
+ {
+ }
+
+ _output.Append("|");
+
+ // Bonus for less operations to optimize the code.
+ countBonus += ((_maxIterationCount - _bf.m_Ticks) / 500.0);
+
+ // Make the AI wait until a solution is found without the penalty.
+ Fitness -= penalty;
+
+ // Check for solution.
+ IsFitnessAchieved();
+
+ Ticks += _bf.m_Ticks;
+ }
+
+
+ // Give a bonus for using multiple memory registers, supporting diversity.
+ if (memoryHash.Count > 1)
+ {
+ countBonus += memoryHash.Count * 100;
+ }
+
+ if (_fitness != Double.MaxValue)
+ {
+ _fitness = Fitness + countBonus + lengthBonus;
+ }
+
+ return _fitness;
+ }
+
+ protected override void RunProgramMethod(string program)
+ {
+ for (int i = 0; i < 99; i++)
+ {
+ // Get input from the user.
+ _console.Clear();
+
+ try
+ {
+ // Run the program.
+ Interpreter bf = new Interpreter(program, () =>
+ {
+ Console.WriteLine();
+ Console.Write(">: ");
+
+ return Byte.Parse(Console.ReadLine());
+ },
+ (b) =>
+ {
+ Console.Write((char)b);
+ });
+
+ bf.Run(_maxIterationCount);
+ }
+ catch
+ {
+ }
+
+ Console.WriteLine();
+ Console.WriteLine("Game Over");
+ }
+ }
+
+ public override string GetConstructorParameters()
+ {
+ return _maxIterationCount + ", " + _trainingCount;
+ }
+
+ #endregion
+ }
+}
@@ -31,6 +31,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
+ <Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@@ -3,17 +3,30 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Configuration;
namespace AIProgrammer.Managers
{
public static class CommonManager
{
+ private static int _brainfuckVersion = Int32.Parse(ConfigurationManager.AppSettings["BrainfuckVersion"]);
+
+ public static string ConvertDoubleArrayToBF(double[] array)
+ {
+ switch (_brainfuckVersion)
+ {
+ case 1: return ConvertDoubleArrayToBFClassic(array);
+ case 2: return ConvertDoubleArrayToBFExtended(array);
+ default: return ConvertDoubleArrayToBFClassic(array);
+ };
+ }
+
/// <summary>
/// Convert a genome (array of doubles) into a Brainfuck program.
/// </summary>
/// <param name="array">Array of double</param>
/// <returns>string - Brainfuck program</returns>
- public static string ConvertDoubleArrayToBF(double[] array)
+ private static string ConvertDoubleArrayToBFClassic(double[] array)
{
StringBuilder sb = new StringBuilder();
@@ -37,7 +50,7 @@ public static string ConvertDoubleArrayToBF(double[] array)
/// </summary>
/// <param name="array">Array of double</param>
/// <returns>string - Brainfuck program</returns>
- public static string ConvertDoubleArrayToBFExtendedType3(double[] array)
+ private static string ConvertDoubleArrayToBFExtended(double[] array)
{
StringBuilder sb = new StringBuilder();
View
@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
- <startup>
- <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
- </startup>
+ <appSettings>
+ <add key="BrainfuckVersion" value="1"/> <!-- 1 = Brainfuck Classic, 2 = Brainfuck Extended Type 3 (Faster) -->
+ </appSettings>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+ </startup>
</configuration>
View
@@ -24,6 +24,8 @@ namespace AIProgrammer
/// </summary>
class Program
{
+ #region Private Variables
+
private static GA _ga = null; // Our genetic algorithm instance.
private static double _bestFitness = 0; // Best fitness so far.
private static double _bestTrueFitness = 0; // Best true fitness so far, used to determine when a solution is found.
@@ -33,13 +35,18 @@ class Program
private static int _bestTicks = 0; // Number of instructions executed by the best program.
private static DateTime _bestLastChangeDate = DateTime.Now; // Time of last improved evolution.
private static DateTime _startTime = DateTime.Now; // Time the program was started.
+ private static double _targetFitness = 0; // Used for displaying the tar
+
+ #endregion
+
+ #region Genetic Algorithm Settings
private static double _crossoverRate = 0.70; // Percentage chance that a child genome will use crossover of two parents.
private static double _mutationRate = 0.01; // Percentage chance that a child genome will mutate a gene.
private static int _genomeSize = 250; // Number of programming instructions in generated program (size of genome array).
private static int _maxIterationCount = 2000; // Max iterations a program may run before being killed (prevents infinite loops).
- private static string _targetString = "hi"; // Target string to generate a program to print.
- private static double _targetFitness = 0;
+
+ #endregion
/// <summary>
/// Selects the type of fitness algorithm to use (Hello World solutions, Calculation solutions, etc).
@@ -56,7 +63,7 @@ class Program
/// <returns>IFitness</returns>
private static IFitness GetFitnessMethod()
{
- return new StringOptimizedFitness(_ga, _maxIterationCount, _targetString);
+ return new StringOptimizedFitness(_ga, _maxIterationCount, "hi");
}
#region Worker Methods

0 comments on commit fd56880

Please sign in to comment.