commit 55b6393437614eb317872e403689b1e0729bf9b3 Author: Patrick Stevens Date: Fri Sep 20 21:08:05 2013 +0100 Upload complete Crossflip project I used Subversion to create my Crossflip solution, but my account on that repository expired; I am transferring it to Github. diff --git a/Crossflip.sln b/Crossflip.sln new file mode 100755 index 0000000..dd78b20 --- /dev/null +++ b/Crossflip.sln @@ -0,0 +1,53 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crossflip", "Crossflip\Crossflip.csproj", "{1615F784-B0A8-4C42-A5EC-5C5508D9BCDD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A831CCD7-AEE0-4AA4-BD15-82179BC646CD}" + ProjectSection(SolutionItems) = preProject + Crossflip.vsmdi = Crossflip.vsmdi + GaussianEliminationAlgorithm.txt = GaussianEliminationAlgorithm.txt + Local.testsettings = Local.testsettings + TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testing", "Testing\Testing.csproj", "{188CB73D-FA28-4C39-9C01-C95B301AF70A}" +EndProject +Global + GlobalSection(TestCaseManagementSettings) = postSolution + CategoryFile = Crossflip.vsmdi + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1615F784-B0A8-4C42-A5EC-5C5508D9BCDD}.Debug|Any CPU.ActiveCfg = Debug|x86 + {1615F784-B0A8-4C42-A5EC-5C5508D9BCDD}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {1615F784-B0A8-4C42-A5EC-5C5508D9BCDD}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {1615F784-B0A8-4C42-A5EC-5C5508D9BCDD}.Debug|x86.ActiveCfg = Debug|x86 + {1615F784-B0A8-4C42-A5EC-5C5508D9BCDD}.Debug|x86.Build.0 = Debug|x86 + {1615F784-B0A8-4C42-A5EC-5C5508D9BCDD}.Release|Any CPU.ActiveCfg = Release|x86 + {1615F784-B0A8-4C42-A5EC-5C5508D9BCDD}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {1615F784-B0A8-4C42-A5EC-5C5508D9BCDD}.Release|Mixed Platforms.Build.0 = Release|x86 + {1615F784-B0A8-4C42-A5EC-5C5508D9BCDD}.Release|x86.ActiveCfg = Release|x86 + {1615F784-B0A8-4C42-A5EC-5C5508D9BCDD}.Release|x86.Build.0 = Release|x86 + {188CB73D-FA28-4C39-9C01-C95B301AF70A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {188CB73D-FA28-4C39-9C01-C95B301AF70A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {188CB73D-FA28-4C39-9C01-C95B301AF70A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {188CB73D-FA28-4C39-9C01-C95B301AF70A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {188CB73D-FA28-4C39-9C01-C95B301AF70A}.Debug|x86.ActiveCfg = Debug|Any CPU + {188CB73D-FA28-4C39-9C01-C95B301AF70A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {188CB73D-FA28-4C39-9C01-C95B301AF70A}.Release|Any CPU.Build.0 = Release|Any CPU + {188CB73D-FA28-4C39-9C01-C95B301AF70A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {188CB73D-FA28-4C39-9C01-C95B301AF70A}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {188CB73D-FA28-4C39-9C01-C95B301AF70A}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Crossflip.vsmdi b/Crossflip.vsmdi new file mode 100755 index 0000000..169b1bd --- /dev/null +++ b/Crossflip.vsmdi @@ -0,0 +1,9 @@ + + + + Tests the addition of IEquations in a SystemOfEquations. + + + + + \ No newline at end of file diff --git a/Crossflip/Crossflip.csproj b/Crossflip/Crossflip.csproj new file mode 100755 index 0000000..4f13df9 --- /dev/null +++ b/Crossflip/Crossflip.csproj @@ -0,0 +1,58 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {1615F784-B0A8-4C42-A5EC-5C5508D9BCDD} + Exe + Properties + Crossflip + Crossflip + v4.0 + Client + 512 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + + + prompt + 4 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Crossflip/Program.cs b/Crossflip/Program.cs new file mode 100755 index 0000000..a351416 --- /dev/null +++ b/Crossflip/Program.cs @@ -0,0 +1,1300 @@ +/* + * Crossflip solver + * www.hacker.org/cross + * (C) Patrick Stevens 2012, aka. laz0r + * Version 2.0.1.1 + * + * Changes: + * In 2.0.1.1: + * Converted compile-time switches for benchmarking/single level solving into runtime switches + * In 2.0.1: + * Added in parallel packed int class, which is marginally faster + * Put backSubstitute into SystemOfEquations rather than being a separate method + * In 2.0.1 (RC): + * Bugfixes and cleaning up of Program so it contains fewer data manipulators + * In 2.0.1 (alpha): + * Added PackedInt storage of equations for a 2/5 speedup + * In 2.0.0 (alpha): + * Encapsulated behaviour of systems of equations into interfaces, one for a single equation and one for a system of such equations, so that internal behaviour is totally separate from external. + * Also added a small but extensible test suite + * In 1.1.2: + * Added get/set board to file functionality, so that we don't need to keep requesting boards from the server but can reproduce them locally + * In 1.1.1: + * Added error handling around submission of a solution, so that if the solution submission fails we keep trying it rather than re-solving the level + * In 1.1.0: + * improved memory access (using ref parameter passing) + * error handling so that if network connectivity is lost, program picks up where it left off + * two instances of this program can now be run concurrently (on different machines), as + * submission of solutions now specifies the level being submitted + * + * Main body of program is an implementation of Gaussian elimination over GF(2). + * + * TODO: + * check whether any of the SystemOfEquations descendants would be more efficient if they implemented gaussianEliminate or backSubstitute + * + * */ + +//#define doingStuffFromFile + +//#define writeOutDebugs + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace Crossflip +{ + + /// + /// On a board, whether a cell is lit (On), unlit (Off) or greyed-out as Unselectable. + /// + public enum CellState { On = 1, Off = 0, Unselectable = 2 }; + + /// + /// interface all Equations must match (ie. structures which represent one row of the Gaussian elimination matrix) + /// + public interface IEquation + { + byte coefficient(int index); + void add(IEquation newEqn); + byte[] ToByteArray(); + byte RHS(); + } + + /// + /// interface all systems of equations must match (these will probably be represented internally by IEquations, but all that is specified here is that they must be able to interact via IEquations) + /// + public interface IEquationSystem + { + byte coefficient(int row, int col); + + void gaussianEliminate(); + + byte[] backSubstitute(); + + void swapTwoEquations(int a, int b); + + byte[] nthRowOfEquations(int n); + + void replaceEquation(int rowNum, IEquation newEquation); + + void addTo(int rowAddedToIndex, int rowAddingIndex); + } + + /// + /// Base class for a System of Equations, based around an array of IEquation[] for internal storage + /// + public abstract class SystemOfEquations : IEquationSystem + { + /// + /// The number of equations which the system is storing + /// + public int numberOfEquations + { + get { return _equationSystem.Length; } + } + + protected IEquation[] _equationSystem; + + public IEquation[] EquationSystem + { get { return _equationSystem; } } + + public byte[,] LHS + { + get + { + byte[,] ans = new byte[numberOfEquations, numberOfEquations]; + for (int i = 0; i < numberOfEquations; i++) + { + byte[] currentRow = _equationSystem[i].ToByteArray(); + for (int j = 0; j < numberOfEquations; j++) + ans[i, j] = currentRow[j]; + } + return ans; + } + } + + public byte[] RHS + { + get + { + byte[] ans = new byte[numberOfEquations]; + for (int i = 0; i < numberOfEquations; i++) + ans[i] = _equationSystem[i].RHS(); + return ans; + } + } + + /// + /// Finds the coefficient (1 or 0) of the col'th variable in the row'th equation + /// + /// Which row the variable is in + /// Which variable we need + /// The coefficient of that variable + public byte coefficient(int row, int col) + { + return _equationSystem[row].coefficient(col); + } + + /// + /// Swaps the specified equations in both LHS and RHS + /// + /// the index of one of the rows + /// the index of the other row + virtual public void swapTwoEquations(int a, int b) + { + IEquation equ = _equationSystem[a]; + _equationSystem[a] = _equationSystem[b]; + _equationSystem[b] = equ; + } + + /// + /// Fetches a certain row of the LHS + /// + /// which row to fetch + /// the coefficients of the given row + virtual public byte[] nthRowOfEquations(int n) + { + return _equationSystem[n].ToByteArray(); + } + + /// + /// After gaussian elimination, returns the solution to the system of equations + /// + virtual public byte[] backSubstitute() + { + byte[] result = new byte[numberOfEquations]; + byte[] tempRHS = RHS; + + for (int row = numberOfEquations - 1; row >= 0; row--) + { + //eliminate matrix[row][row] + if (coefficient(row, row) == 0) + { + result[row] = 0; //don't need to eliminate; 0 is already cancelling out + } + else + { + //eliminate this 1 + if (tempRHS[row] == 0) + { + //then substituting this variable will not affect the matrix + result[row] = 0; + } + else //the result of this line is 1, so we're going to have to switch any other rows incorporating this variable in equals + { + for (int i = 0; i < row; i++) + { + if (coefficient(i, row) == 1) + tempRHS[i] ^= 1; //flip that bit + } + result[row] = 1; + } + } + } + return result; + } + + /// + /// Replaces a given equation with a new one + /// + /// the index of the equation to replace + /// the new equation + virtual public void replaceEquation(int rowNum, IEquation newEquation) + { + _equationSystem[rowNum] = newEquation; + } + + /// + /// The equivalent of matrix[rowAddedToIndex] += matrix[rowAddingIndex] (mod 2) - does this for both LHS and RHS + /// + /// the index of the row which is being added to + /// the index of the operand + virtual public void addTo(int rowAddedToIndex, int rowAddingIndex) + { + _equationSystem[rowAddedToIndex].add(_equationSystem[rowAddingIndex]); + } + + //from IEquations + /// + /// Performs Gaussian elimination on the equations so that they are in upper triangular with as many 1's on the diagonal as possible + /// + virtual public void gaussianEliminate() + { + //see page 2 of http://dde.binghamton.edu/filler/mct/hw/1/assignment.pdf for the algorithm; alternatively, look at the GaussianEliminationAlgorithm text file in this solution + int n = numberOfEquations; + int m = numberOfEquations; + int col; + int row = 0; + + for (col = 0; col < m; col++) //for every column [they work on a non-augmented matrix] + { + int maxrow = row; + for (int k = n - 1; k >= row; k--) + { + if (coefficient(k, col) == 1) + { + maxrow = k; + break; + } + + } + + if (coefficient(maxrow, col) == 1) + { + //swap rows i and maxi in LHS and RHS, but do not change the value of i + swapTwoEquations(maxrow, row); + for (int u = row + 1; u < m /*-1*/; u++) + { + //Add A[u,i] * row i to row u, do this for BOTH, matrix A and RHS vector b + if (coefficient(u, col) == 1) + addTo(u, row); + } + } + else //more than one solution exists + row--; + + row++; + } + + //we'll skip the checking whether there is a solution, because we know there is one + + //now, we've eliminated, but we need to make sure there are 1's on the diagonals + + for (int i = 0; i < n; i++) + { + if (coefficient(i, i) == 1) continue; + + //now one of the bottom rows will be 0's + //so shove every row downwards + + for (int rownum = n - 1; rownum > i; rownum--) + swapTwoEquations(rownum, rownum - 1); + + } + + return; + } + } + + /// + /// An example (which is currently our best) of an equation with the coefficients stored as an array of byte + /// + public class ByteStoredEquation : IEquation + { + private byte[] _LHS; + private byte _RHS; + + public int numberOfCoefficients + { get { return _LHS.Length; } } + + public byte coefficient(int index) + { + return _LHS[index]; + } + + public void add(IEquation newEquation) + { + for (int i = 0; i < numberOfCoefficients; i++) + { + _LHS[i] ^= newEquation.coefficient(i); + } + + _RHS ^= newEquation.RHS(); + } + + public void xor(int leftMostIndex) + { + for (int i = leftMostIndex; i < numberOfCoefficients; i++) + _LHS[i] ^= 1; + _RHS ^= 1; + } + + public byte[] ToByteArray() + { + return _LHS; + } + + public byte RHS() + { + return _RHS; + } + + public ByteStoredEquation(byte[] coefficients, byte rhs) + : base() + { + _RHS = rhs; + _LHS = coefficients; + } + } + + /// + /// A system of equations based around the ByteStoredEquation, which should demonstrate that almost nothing needs to be done to the SystemOfEquations to instantiate it, other than to implement a constructor - although efficiency may well require rewriting other functions + /// + public class ByteStoredSystemOfEquations : SystemOfEquations + { + public ByteStoredSystemOfEquations(Board b) + : base() + { + _equationSystem = new IEquation[b.boardLength - b.numberOfUnselectables]; + + //we will need to scale all the indices down so that whenever there is an unselectable square, the subsequent indices are subtracted by 1 + + //start populating result matrix + for (int index = 0; index < b.boardLength; index++) + { + byte[] result = new byte[b.boardLength - b.numberOfUnselectables]; + + Point pointConsidering = new Point(index, ref b); + if (b.square(pointConsidering.firstDimension, pointConsidering.secondDimension) == CellState.Unselectable) + continue; + //get the list of points which will flip the current point + Point[] pointsAffected = b.getPositionsFlippedByGivenPosition(pointConsidering); + int[] indicesAffected = new int[pointsAffected.Length]; + for (int i = 0; i < pointsAffected.Length; i++) + { + int pos = pointsAffected[i].ToZeroIndexedPosition(); + indicesAffected[i] = b.scaledIndices[pos]; + } + + foreach (int i in indicesAffected) + result[i] = 1; + + _equationSystem[b.scaledIndices[index]] = new ByteStoredEquation(result, Convert.ToByte((b.square(pointConsidering.firstDimension, pointConsidering.secondDimension) == CellState.Off))); + } + } + } + + /// + /// A silly class which stores equations as characters, just to demonstrate the power of the interface - note that there is no other code than the constructor + /// + public class CharStoredSystemOfEquations : SystemOfEquations + { + public CharStoredSystemOfEquations(Board b) + : base() + { + _equationSystem = new IEquation[b.boardLength - b.numberOfUnselectables]; + + //we will need to scale all the indices down so that whenever there is an unselectable square, the subsequent indices are subtracted by 1 + + //start populating result matrix + for (int index = 0; index < b.boardLength; index++) + { + byte[] result = new byte[b.boardLength - b.numberOfUnselectables]; + + Point pointConsidering = new Point(index, b); + //get the list of points which will flip the current point + Point[] pointsAffected = b.getPositionsFlippedByGivenPosition(pointConsidering); + int[] indicesAffected = new int[pointsAffected.Length]; + for (int i = 0; i < pointsAffected.Length; i++) + { + int pos = pointsAffected[i].ToZeroIndexedPosition(); + indicesAffected[i] = b.scaledIndices[pos]; + } + + foreach (int i in indicesAffected) + result[i] = 1; + + _equationSystem[b.scaledIndices[index]] = new CharStoredEquation(result, Convert.ToByte((b.square(pointConsidering.firstDimension, pointConsidering.secondDimension) == CellState.Off))); + } + } + } + + /// + /// the base class for an equation stored as characters + /// + public class CharStoredEquation : IEquation + { + private char[] _LHS; + private char _RHS; + + private byte charToByte(char ch) + { + return (byte)((byte)(ch) % 2); + } + + public int numberOfCoefficients + { get { return _LHS.Length / 2; } } + + public byte coefficient(int index) + { + return charToByte(_LHS[index * 2]); + } + + public void add(IEquation newEquation) + { + for (int i = 0; i < numberOfCoefficients; i++) + { + _LHS[i * 2] += (char)(newEquation.coefficient(i)); + } + _RHS += (char)(newEquation.RHS()); + } + + public void xor(int leftMostIndex) + { + for (int i = leftMostIndex; i < _LHS.Length; i++) + _LHS[i] += (char)1; + } + + public byte[] ToByteArray() + { + byte[] result = new byte[numberOfCoefficients]; + for (int i = 0; i < numberOfCoefficients * 2; i += 2) + result[i / 2] = charToByte(_LHS[i]); + return result; + } + + public byte RHS() + { + return charToByte(_RHS); + } + + public CharStoredEquation(byte[] coefficients, byte rhs) + : base() + { + _RHS = (char)(rhs + 60); + _LHS = new char[coefficients.Length * 2]; + for (int i = 0; i < numberOfCoefficients; i++) + _LHS[i * 2] = (char)(coefficients[i] + 60); + } + } + + public class PackedIntStoredEquation : IEquation + { + byte _RHS; + public byte RHS() + { + return _RHS; + } + + private int _numberOfCoefficients; + public int numberOfCoefficients + { + get { return _numberOfCoefficients; } + } + + ulong[] _LHS; + public byte[] ToByteArray() + { + byte[] result = new byte[numberOfCoefficients]; + for (int i = 0; i < numberOfCoefficients; i++) + result[i] = coefficient(i); + return result; + } + + private int whichByteOfUlongArr(int pos) + { + return (int)Math.Floor((float)pos / 64); + } + + public byte coefficient(int pos) + { + //work out which element in the _LHS it's stored in + int elem = whichByteOfUlongArr(pos); + + //now it's in the elemth position + int howFarAlong = 63 - (pos % 64); + return (byte)((_LHS[elem] >> howFarAlong) & 1); + } + + public void add(PackedIntStoredEquation eq) + { + _RHS ^= eq.RHS(); + for (int i = 0; i < _LHS.Length; i++) + _LHS[i] ^= eq._LHS[i]; + } + + public void add(IEquation eq) + { + if (eq.GetType() == this.GetType()) + add((PackedIntStoredEquation)eq); + else + throw new Exception("Not implemented!"); + } + + public PackedIntStoredEquation(byte[] coefficients, byte rhs) + : base() + { + _RHS = rhs; + _numberOfCoefficients = coefficients.Length; + _LHS = new ulong[(int)Math.Ceiling((float)_numberOfCoefficients / 64)]; + + for (int i = 0; i < numberOfCoefficients; i++) + { + int howFarAlong = 63 - (i % 64); + _LHS[whichByteOfUlongArr(i)] ^= ((ulong)coefficients[i] << howFarAlong); + } + } + } + + public class PackedIntStoredSystemOfEquations : SystemOfEquations + { + public PackedIntStoredSystemOfEquations(Board b) + : base() + { + _equationSystem = new IEquation[b.boardLength - b.numberOfUnselectables]; + + //we will need to scale all the indices down so that whenever there is an unselectable square, the subsequent indices are subtracted by 1 + + //start populating result matrix + for (int index = 0; index < b.boardLength; index++) + { + if (b.locationsOfUnselectables.Contains(index)) + continue; + + byte[] result = new byte[b.boardLength - b.numberOfUnselectables]; + + Point pointConsidering = new Point(index, b); + //get the list of points which will flip the current point + Point[] pointsAffected = b.getPositionsFlippedByGivenPosition(pointConsidering); + int[] indicesAffected = new int[pointsAffected.Length]; + for (int i = 0; i < pointsAffected.Length; i++) + { + int pos = pointsAffected[i].ToZeroIndexedPosition(); + indicesAffected[i] = b.scaledIndices[pos]; + } + + foreach (int i in indicesAffected) + result[i] = 1; + + _equationSystem[b.scaledIndices[index]] = new PackedIntStoredEquation(result, Convert.ToByte((b.square(pointConsidering.firstDimension, pointConsidering.secondDimension) == CellState.Off))); + } + } + } + + public abstract class ParallelSystemOfEquations : SystemOfEquations + { + override public void gaussianEliminate() + { + //see page 2 of http://dde.binghamton.edu/filler/mct/hw/1/assignment.pdf for the algorithm; alternatively, look at the GaussianEliminationAlgorithm text file in this solution + int n = numberOfEquations; + int m = numberOfEquations; + int col; + int row = 0; + + for (col = 0; col < m; col++) //for every column [they work on a non-augmented matrix] + { + int maxrow = row; + for (int k = n - 1; k >= row; k--) + { + if (coefficient(k, col) == 1) + { + maxrow = k; + break; + } + + } + + if (coefficient(maxrow, col) == 1) + { + //swap rows i and maxi in LHS and RHS, but do not change the value of i + swapTwoEquations(maxrow, row); + Parallel.For(row + 1, m, u => { if (coefficient(u, col) == 1) addTo(u, row); }); + /*for (int u = row + 1; u < m; u++) + { + //Add A[u,i] * row i to row u, do this for BOTH, matrix A and RHS vector b + if (coefficient(u, col) == 1) + addTo(u, row); + }*/ + } + else //more than one solution exists + row--; + + row++; + } + + //we'll skip the checking whether there is a solution, because we know there is one + + //now, we've eliminated, but we need to make sure there are 1's on the diagonals + + for (int i = 0; i < n; i++) + { + if (coefficient(i, i) == 1) continue; + + //now one of the bottom rows will be 0's + //so shove every row downwards + + for (int rownum = n - 1; rownum > i; rownum--) + swapTwoEquations(rownum, rownum - 1); + + } + + return; + } + } + + public class PackedIntStoredParallelSystemOfEquations : ParallelSystemOfEquations + { + public PackedIntStoredParallelSystemOfEquations(Board b) + : base() + { + _equationSystem = new IEquation[b.boardLength - b.numberOfUnselectables]; + + //we will need to scale all the indices down so that whenever there is an unselectable square, the subsequent indices are subtracted by 1 + + //start populating result matrix + for (int index = 0; index < b.boardLength; index++) + { + if (b.locationsOfUnselectables.Contains(index)) + continue; + + byte[] result = new byte[b.boardLength - b.numberOfUnselectables]; + + Point pointConsidering = new Point(index, b); + //get the list of points which will flip the current point + Point[] pointsAffected = b.getPositionsFlippedByGivenPosition(pointConsidering); + int[] indicesAffected = new int[pointsAffected.Length]; + for (int i = 0; i < pointsAffected.Length; i++) + { + int pos = pointsAffected[i].ToZeroIndexedPosition(); + indicesAffected[i] = b.scaledIndices[pos]; + } + + foreach (int i in indicesAffected) + result[i] = 1; + + _equationSystem[b.scaledIndices[index]] = new PackedIntStoredEquation(result, Convert.ToByte((b.square(pointConsidering.firstDimension, pointConsidering.secondDimension) == CellState.Off))); + } + } + } + + /// + /// The class used to represent the game board. + /// + public class Board + { + + private int level; + private int _firstIndexDimension; + private int[] _unselectables; + private int[] _scaledIndices; + + CellState[,] _internalBoard; + + public int firstIndexDimension + { + get { return this._firstIndexDimension; } + } + public int secondIndexDimension + { + get { return boardLength / firstIndexDimension; } + } + + public int boardLength + { + get { return this._internalBoard.Length; } + } + + private void setMainVariables(string boardVariables) + { + string choppedBoardInit = boardVariables.Substring(boardVariables.IndexOf("\"") + 1); + choppedBoardInit = choppedBoardInit.Remove(choppedBoardInit.IndexOf("\"")); + string[] boardState = choppedBoardInit.Split(','); + _internalBoard = new CellState[boardState.Length, boardState[0].Length]; + _firstIndexDimension = boardState.Length; + + for (int i = 0; i < boardState.Length; i++) + for (int j = 0; j < boardState[0].Length; j++) + switch (boardState[i][j]) + { + case '0': _internalBoard[i, j] = CellState.On; break; + case '1': _internalBoard[i, j] = CellState.Off; break; + default: _internalBoard[i, j] = CellState.Unselectable; break; + } + + string levelStr = boardVariables.Substring(boardVariables.LastIndexOf(" ") + 1); + level = Convert.ToInt32(levelStr.Remove(levelStr.Length - 1)); + + + #region unselectablestuff + //populate unselectables + _unselectables = positionsOnBoardWhichMustBeZero(); + int[] totalNumberOfUnselectablesEncountered = new int[this.boardLength]; + + //populate array of totalNumberOfUns... + for (int index = 0; index < this.boardLength; index++) + { + Point pointConsidering = new Point(index, this); + if (square(pointConsidering.firstDimension, pointConsidering.secondDimension) == CellState.Unselectable) + { + if (index == 0) + totalNumberOfUnselectablesEncountered[index] = 1; + else + totalNumberOfUnselectablesEncountered[index] = totalNumberOfUnselectablesEncountered[index - 1] + 1; + continue; + } + + if (index != 0) totalNumberOfUnselectablesEncountered[index] = totalNumberOfUnselectablesEncountered[index - 1]; + } + + _scaledIndices = new int[this.boardLength]; + _scaledIndices[0] = totalNumberOfUnselectablesEncountered[0]; + for (int i = 1; i < this.boardLength; i++) + _scaledIndices[i] = i - totalNumberOfUnselectablesEncountered[i]; + #endregion + } + + /// + /// For a given position, returns an array of all the points which would be flipped if the user clicked on that position. + /// + /// the point the user clicked + /// the array of all the points flipped + public Point[] getPositionsFlippedByGivenPosition(Point pointClicked) + { + if (square(pointClicked.firstDimension, pointClicked.secondDimension) == CellState.Unselectable) + return new Point[0]; + + int indexOfMaximumFirstDimension = pointClicked.firstDimension; + while ((indexOfMaximumFirstDimension < firstIndexDimension) && (square(indexOfMaximumFirstDimension, pointClicked.secondDimension) != CellState.Unselectable)) + indexOfMaximumFirstDimension++; + // if (indexOfMaximumFirstDimension != inputBoard.firstIndexDimension) + indexOfMaximumFirstDimension--; + + int indexOfMinimumFirstDimension = pointClicked.firstDimension; + while ((indexOfMinimumFirstDimension >= 0) && (square(indexOfMinimumFirstDimension, pointClicked.secondDimension) != CellState.Unselectable)) + indexOfMinimumFirstDimension--; + indexOfMinimumFirstDimension++; + + int indexOfMinimumSecondDimension = pointClicked.secondDimension; + while ((indexOfMinimumSecondDimension >= 0) && (square(pointClicked.firstDimension, indexOfMinimumSecondDimension) != CellState.Unselectable)) + indexOfMinimumSecondDimension--; + indexOfMinimumSecondDimension++; + + int indexOfMaximumSecondDimension = pointClicked.secondDimension; + while ((indexOfMaximumSecondDimension < secondIndexDimension) && (square(pointClicked.firstDimension, indexOfMaximumSecondDimension) != CellState.Unselectable)) + indexOfMaximumSecondDimension++; + // if (indexOfMaximumSecondDimension != inputBoard.secondIndexDimension) + indexOfMaximumSecondDimension--; + + Point[] result = new Point[(indexOfMaximumSecondDimension - indexOfMinimumSecondDimension) + (indexOfMaximumFirstDimension - indexOfMinimumFirstDimension) + 1]; + int currResultPtr = 0; + + for (int i = indexOfMinimumFirstDimension; i <= indexOfMaximumFirstDimension; i++) + { + result[currResultPtr] = new Point(i, pointClicked.secondDimension, this); + currResultPtr++; + } + for (int j = indexOfMinimumSecondDimension; j <= indexOfMaximumSecondDimension; j++) + { + if (j == pointClicked.secondDimension) continue; + result[currResultPtr] = new Point(pointClicked.firstDimension, j, this); + currResultPtr++; + } + + return result; + } + + + /// + /// Constructor for the Board + /// + /// Either a filepath or the source of the hacker.org page + public Board(string boardVariables) + : base() + { + if (boardVariables[boardVariables.Length - 4] == '.') + //it's a filepath + { + string[] fromFile = new string[0]; + + fromFile = File.ReadAllLines(boardVariables.ToString()); + + //file format was the 1's and 0's of the board with commas inserted, then a newline, + setMainVariables("")); + string boardData = endChoppedOff.Substring(endChoppedOff.IndexOf("