public Block(BlockData options) { BlockLength = options.length; BlockStart = options.start; BlockColour = options.colour; _blockClues = new Clues(); }
/// <summary> /// Determines which clues can be which blocks /// </summary> /// <remarks> /// Given a collection of blocks and a collection of clues, it is possible to determine that certain clues /// simply cannot be certain blocks (because they're too long or in the wrong place) /// This is all now done by the BlockIdentifier class. /// </remarks> public static void IdentifyBlocks(Grid grid, Clues clues, Blocks blocks, Spaces spaces, int element, int elementLength, bool isRow) { if (blocks.GetBlockCount() > 0 && clues.GetClueCount() > 0 && !clues.AllCluesSolved()) { BlockIdentifier bId = new BlockIdentifier(clues, blocks, spaces, elementLength); bId.IdentifyBlocks(); //now print the clues permitted for each block - this is for checking string outputstring; if (isRow) { Console.WriteLine("Row {0}, {1} clues and {2} blocks", element, clues.GetClueCount(), blocks.GetBlockCount()); } else { Console.WriteLine("Col {0}, {1} clues and {2} blocks", element, clues.GetClueCount(), blocks.GetBlockCount()); } for (int blockNo = 0; blockNo < blocks.GetBlockCount(); blockNo++) { outputstring = "Block : " + blockNo + ": "; for (int clueNo = 0; clueNo < clues.GetClueCount(); clueNo++) { if (blocks.GetBlock(blockNo).AllClues().ClueExists(clues.getClue(clueNo))) { outputstring += clueNo + ", "; } } Console.WriteLine(outputstring); } } }
/// <summary> /// A pretty simple method to check if there are certain cells that must be filled in regardless of clue position /// </summary> /// <remarks> /// If the clues are positioned as far left as they can go and then as far right as they can go there may be /// certain cells that will always be covered. /// For example, in an element of 15 cells a clue of length 10 will always cover cells 5 to 9 /// </remarks> private static int Overlap(Grid grid, Clues clues, Blocks blocks, Spaces spaces, int element, int elementLength, bool isRow) { int filled = 0; int firstFree = grid.GetFirstFreeCell(element, isRow); int lastFree = grid.GetLastFreeCell(element, isRow); int rowToUpdate; int colToUpdate; //For a given space, find the clues that HAVE to be in that space and check limits for (int spaceNo = 0; spaceNo < spaces.getSpaceCount(); spaceNo++) { Clues cluesToTest = FindFewestClues(spaces, clues, spaceNo); } for (int clueNo = 0; clueNo < clues.GetClueCount(); clueNo++) { //this needs adapted to take account of filled cells int endClueLeft = firstFree - 1 + clues.GetClueLength(0, clueNo); int startClueRight = lastFree + 1 - clues.GetClueLength(clueNo, clues.GetClueCount() - 1); if (startClueRight <= endClueLeft) { for (int cellNo = startClueRight; cellNo <= endClueLeft; cellNo++) { rowToUpdate = (isRow) ? element : cellNo; colToUpdate = (isRow) ? cellNo : element; if (Update(grid, colToUpdate, rowToUpdate, clues.getClue(clueNo).Colour)) { filled += 1; } } } } return(filled); }
/// <summary> /// Sets each element of the permitted clues array to a new empty Clues object /// </summary> private void InitialisePermittedClues(Clues[] clueList) { for (int i = 0; i < clueList.Length; i++) { clueList[i] = new Clues(); } }
/// <summary> /// Checks that the clue is entirely within a space (i.e. not opposite a cross square) /// </summary> private bool CluesFitInSpaces(Clues clues, Spaces spaces, int[] cluePositions) { for (int clueNo = 0; clueNo < clues.GetClueCount(); clueNo++) { if (!ClueFitsInSpace(clues.getClue(clueNo), spaces, cluePositions[clueNo])) { return(false); } } return(true); }
public static Clues FindFewestClues(Spaces spaces, Clues clues, int clueNo) { Clues resultClues = new Clues(); Spaces spacesBefore = new Spaces(); Space spaceToTest; Spaces spacesAfter = new Spaces(); //now we want to add the clues one by one to spacesAfter. If they won't fit in SpacesAfter then //we move them to spaceToTest and if they won't fit there we move them to spacesBefore return(resultClues); }
/// <summary> /// This class takes a Clues object, a Spaces object and a Blocks object and /// for each block determines which clues that block could legally be /// </summary> public BlockIdentifier(Clues clues, Blocks blocks, Spaces spaces, int elementLength) { _blocks = blocks; _clues = clues; _spaces = spaces; int clueCount = _clues.GetClueCount(); _cluePositions = new int[clueCount]; for (int i = 0; i < clueCount; i++) { _cluePositions[i] = elementLength - _clues.GetClueLength(i); } }
/// <summary> /// The only public method. It iterates through each legal position of the clues and /// checks to see if that arrangement is legal /// </summary> public void IdentifyBlocks() { Clues[] permittedClues = new Clues[_blocks.GetBlockCount()]; //temporary array to hold clues InitialisePermittedClues(permittedClues); //the array initially has null values. This sets them to empty Clues bool positionsToTest = true; while (positionsToTest) { ClearPermittedClues(permittedClues); if (ArrangementIsLegal(permittedClues)) { UpdateBlocks(permittedClues); } positionsToTest = FindNextCluePosition(); } }
public static bool CluesWillFitInSpaces(Clues clues, Spaces spaces, Blocks blocks) { List <Space> spaceList = new List <Space>(); List <Clue> clueList = new List <Clue>(); //first put all the spaces into spacelist //and all the clues into clueList foreach (Space space in spaces) { spaceList.Add(space); } foreach (Clue clue in clues) { clueList.Add(clue); } bool finished = false; while (!finished) { //move the next clue into the last space if (clueList.Count > 0) { spaceList[spaceList.Count - 1].AddClue(clueList[0]); clueList.RemoveAt(0); } for (int i = spaceList.Count - 1; i >= 0; i--) { // for each space, if the clues are too long to fit, move the first one out while (!CluesWillFitInSpace(spaceList[i], blocks)) { //we need to move the first clue to the next space. If we're in space0 we can't and the test fails if (i == 0) { return(false); } spaceList[i - 1].AddClue(spaceList[i].RemoveClue(0)); } } finished = clueList.Count == 0; } return(true); }
/// <summary> /// Deals with clues that are close to the edge of a spaces /// </summary> /// <remarks> /// Example: if we have a clue of length 3 as the first clue and cell 3 is filled in then cell 0 cannot be filled. /// Same can be done at the end of the element. /// </remarks> public static int EdgeProximity(Grid grid, Clues clues, Blocks blocks, Spaces spaces, int element, int elementLength, bool isRow) { int filled = 0; int rowToUpdate; int colToUpdate; bool atStart = true; int fillFrom = -1; int fillTo = -1; //Cross out cells which logically cannot be filled because there's too big a gap between the first block and the edge of the element. if (blocks.GetBlockCount() > 0 && clues.GetClueCount() > 0) { for (int loopCount = 0; loopCount < 2; loopCount++) { if (atStart && blocks.GetBlock(0).BlockColour == clues.getClue(0).Colour) { fillFrom = blocks.GetBlock(0).BlockStart - clues.getClue(0).Number; fillTo = fillFrom + blocks.GetBlock(0).BlockLength - 1; fillFrom = (fillFrom < 0) ? 0 : fillFrom; } else if (!atStart && blocks.GetBlock(blocks.GetBlockCount() - 1).BlockColour == clues.getClue(clues.GetClueCount() - 1).Colour) { fillFrom = blocks.GetBlock(blocks.GetBlockCount() - 1).BlockStart + clues.getClue(clues.GetClueCount() - 1).Number; fillTo = fillFrom + blocks.GetBlock(blocks.GetBlockCount() - 1).BlockLength; fillTo = (fillTo > elementLength - 1) ? elementLength - 1 : fillTo; } if ((fillFrom == 0 && fillTo >= 0) || (fillTo == elementLength && fillFrom <= elementLength)) { for (int cellNo = fillFrom; cellNo <= fillTo; cellNo++) { rowToUpdate = (isRow) ? element : cellNo; colToUpdate = (isRow) ? cellNo : element; if (Update(grid, colToUpdate, rowToUpdate, "cross")) { filled += 1; } } } atStart = false; } } return(filled); }
/// <summary> /// deals with the special case where there is only one clue. /// </summary> /// <remarks> /// This feels as though it should be able to be part of a different method. We'll see. /// </remarks> /// private static int SingleClue(Grid grid, Clues clues, Blocks blocks, Spaces spaces, int element, int elementLength, bool isRow) { int filled = 0; int blocksStart; int blocksEnd; int rowToUpdate; int colToUpdate; Clue clue; //we know there is only one clue. If there is more than one block then the space between them must be filled in. if (blocks.GetBlockCount() > 0 && clues.GetClueCount() == 1) { blocksStart = blocks.GetBlock(0).BlockStart; blocksEnd = blocks.GetBlock(blocks.GetBlockCount() - 1).BlockStart + blocks.GetBlock(blocks.GetBlockCount() - 1).BlockLength - 1; clue = clues.getClue(0); for (int cellNo = grid.GetFirstFreeCell(element, isRow); cellNo <= grid.GetLastFreeCell(element, isRow); cellNo++) { rowToUpdate = (isRow) ? element : cellNo; colToUpdate = (isRow) ? cellNo : element; if (cellNo >= blocksStart && cellNo <= blocksEnd) { if (Update(grid, colToUpdate, rowToUpdate, clue.Colour)) { filled += 1; } } else if (cellNo < blocksEnd - clue.Number + 1 || cellNo > blocksStart + clue.Number - 1) { if (Update(grid, colToUpdate, rowToUpdate, "cross")) { filled += 1; } } } } return(filled); }
public Space(SpaceData options) { SpaceLength = options.length; SpaceStart = options.start; _spaceClues = new Clues(); }