Skip to content

Instantly share code, notes, and snippets.

@Eibwen
Last active February 15, 2024 14:50
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Eibwen/e8fec52e0f5927108d86 to your computer and use it in GitHub Desktop.
Save Eibwen/e8fec52e0f5927108d86 to your computer and use it in GitHub Desktop.
//#define STACKON
//The idea here is to use single byte array to store all the values needed in a logical fashion
// So making my own very vasic objects and pointers
void Main()
{
// (1 << 12).Dump();
// (1 << 10).Dump();
// (2916*2*5).Dump();
// (324*2*2).Dump();
// (2916*2*5 + 324*2*2).Dump();
try
{
//DancingLinksArray dla = new DancingLinksArray(@"C:\Documents and Settings\Netbook\My Documents\LINQPad Queries\Suduku\DancingLinksTable.Binary.bin",
// @"C:\Documents and Settings\Netbook\My Documents\LINQPad Queries\Suduku\DancingLinksTable.Columns.txt");
var dla = new DancingLinksArray();
dla.BuildTableFull();
//dla.TEST_Output();
//string board = "-5------1---934------------86-2-914-94---5-6-1-------2------7---1-6-2-84---38---9";
var board = "85...24..72......9..4.........1.7..23.5...9...4...........8..7..17..........36.4.";
dla.DumpNodeIds();
dla.LoadSudoku(board);
dla.DumpNodeIds();
dla.Search();
// DancingLinksArray dla = new DancingLinksArray();
// dla.TEST_DataStore();
}
catch (Exception ex)
{
DUMPSTACK();
DEBUG.Dump("Debug Str");
ex.ToString().Dump();
throw;
}
}
static string DEBUG;
static Stack<string> DEBUG_STACK = new Stack<string>();
static int DEBUG_COUNT = 0;
[Conditional("STACKON")]
[DebuggerStepThrough]
public static void ADDMESSAGE(string message)
{
DEBUG_STACK.Push(message);
++DEBUG_COUNT;
// if (DEBUG_COUNT % 10 == 0)
// {
// (DEBUG_COUNT + " -- " + DEBUG_STACK.Count).Dump();
// }
// if (DEBUG_COUNT % 50 == 0)
// {
// DEBUG_STACK.Dump();
// //throw new NoNullAllowedException();
// }
message.Dump();
if (DEBUG_COUNT > 1000) throw new NoNullAllowedException();
}
[Conditional("STACKON")]
[DebuggerStepThrough]
public static void ENDMESSAGE(string message)
{
string msg = DEBUG_STACK.Pop();
if (msg != message) throw new DivideByZeroException("MISMATCH: " + msg + " == " + message);
}
[Conditional("STACKON")]
[DebuggerStepThrough]
public static void DUMPSTACK()
{
DEBUG_STACK.Dump();
}
/*
TODO For full project, to convertion to mirc script
--1. Output solution result
2. Generate full table so i don't need an attachment
3. Write converter to do most of the conversion for me
Also any have calc(int i) return i; function to ease conversion
I think most other things should be fairly straight forward...
*/
public class DancingLinksArray
{
public DancingLinksArray()
{
//324 column headers needed, 729 rows...
//2916 nodes (Not sure if this includes heads or not)
// SO I think i need 12 bits per pointer... Does mirc support bitshifting?
//For ease, lets say 16 bits per pointer...
// Each node has: 5 pointers
// Headers add: 1 pointer + count (10 bits)
// --WAIT WHAT POINTER?? Just a Name and a Count
//SO:
// Nodes need 5*2 bytes
// Headers need 7*2 bytes
//Approx 29,160 bytes (maybe plus 1,296)
DATA = new ArrayDataStore();
//So Ids 1 - 324 are columns
}
public DancingLinksArray(string binaryFilename, string columnsFilename)
{
DATA = new ArrayDataStore(binaryFilename, columnsFilename);
}
short HEAD = 1;
IDataStores DATA;
public void Search()
{
Search(0);
}
private void Search(int depth)
{
ADDMESSAGE("Search-" + depth);
if (DATA.GetNodePointer(HEAD, 'e') == HEAD)
{
"SOLUTION FOUND".Dump();
("Depth: " + depth).Dump();
//SolutionSet.Dump(1);
// if (SolutionFound != null)
// {
// //TODO if this returns false, stop the searching...
// SolutionFound(SolutionSet.AsEnumerable());
// }
DATA.OutputSolutionSet().Dump();
return;
}
short column = FindColumnToCover();
Cover(column);
var southPointer = DATA.GetNodePointer(column, 's');
while (southPointer != column)
{
ADDMESSAGE("Search-" + depth + "-#" + southPointer);
//Recurse this step
DATA.PushToSolutionSet(southPointer);
var eastPointer = DATA.GetNodePointer(southPointer, 'e');
while (eastPointer != southPointer)
{
Cover(DATA.GetNodePointer(eastPointer, 'h'));
eastPointer = DATA.GetNodePointer(eastPointer, 'e');
}
Search(depth + 1);
//Backtrack this step
//Remove node from it, redundant?
DATA.PopFromSolutionSet();
//column = node.Header; //Also redundant?
var westPointer = DATA.GetNodePointer(southPointer, 'w');
while (westPointer != southPointer)
{
Uncover(DATA.GetNodePointer(westPointer, 'h'));
westPointer = DATA.GetNodePointer(westPointer, 'w');
}
ENDMESSAGE("Search-" + depth + "-#" + southPointer);
southPointer = DATA.GetNodePointer(southPointer, 's');
}
Uncover(column);
ENDMESSAGE("Search-" + depth);
return;
}
public short FindColumnToCover()
{
ADDMESSAGE("FindColumnToCover");
short targetColumn = -1;
int lowestSize = 9000;
var eastPointer = DATA.GetNodePointer(HEAD, 'e');
while (eastPointer != HEAD)
{
ADDMESSAGE("FindColumnToCover-#" + eastPointer);
////"find".Dump();
////eastPointer.Dump();
var count = DATA.GetHeaderCount(eastPointer);
if (count < lowestSize)
{
ADDMESSAGE("FindColumnToCover-Set-" + count + "-#" + eastPointer);
lowestSize = count;
targetColumn = eastPointer;
ENDMESSAGE("FindColumnToCover-Set-" + count + "-#" + eastPointer);
}
ENDMESSAGE("FindColumnToCover-#" + eastPointer);
eastPointer = DATA.GetNodePointer(eastPointer, 'e');
}
ENDMESSAGE("FindColumnToCover");
return targetColumn;
}
private void Cover(short column)
{
ADDMESSAGE("Cover-#" + column);
DEBUG = DATA.GetNodeString(column);
DATA.SetNodePointer(DATA.GetNodePointer(column, 'e'), 'w',
DATA.GetNodePointer(column, 'w'));
DATA.SetNodePointer(DATA.GetNodePointer(column, 'w'), 'e',
DATA.GetNodePointer(column, 'e'));
////"cover".Dump();
var southPointer = DATA.GetNodePointer(column, 's');
while (southPointer != column)
{
var eastPointer = DATA.GetNodePointer(southPointer, 'e');
while (eastPointer != southPointer)
{
DATA.SetNodePointer(DATA.GetNodePointer(eastPointer, 'n'), 's',
DATA.GetNodePointer(eastPointer, 's'));
DATA.SetNodePointer(DATA.GetNodePointer(eastPointer, 's'), 'n',
DATA.GetNodePointer(eastPointer, 'n'));
DATA.DecreaseHeaderCount(DATA.GetNodePointer(eastPointer, 'h'));
eastPointer = DATA.GetNodePointer(eastPointer, 'e');
}
southPointer = DATA.GetNodePointer(southPointer, 's');
}
ENDMESSAGE("Cover-#" + column);
}
public void Uncover(short column)
{
ADDMESSAGE("Uncover-#" + column);
////"uncover".Dump();
var northPointer = DATA.GetNodePointer(column, 'n');
while (northPointer != column)
{
var westPointer = DATA.GetNodePointer(northPointer, 'w');
while (westPointer != northPointer)
{
DATA.IncreaseHeaderCount(DATA.GetNodePointer(westPointer, 'h'));
DATA.SetNodePointer(DATA.GetNodePointer(westPointer, 'n'), 's',
westPointer);
DATA.SetNodePointer(DATA.GetNodePointer(westPointer, 's'), 'n',
westPointer);
westPointer = DATA.GetNodePointer(westPointer, 'w');
}
northPointer = DATA.GetNodePointer(northPointer, 'n');
}
DATA.SetNodePointer(DATA.GetNodePointer(column, 'e'), 'w',
column);
DATA.SetNodePointer(DATA.GetNodePointer(column, 'w'), 'e',
column);
ENDMESSAGE("Uncover-#" + column);
}
//Id 1 is head...
private short NextNodeId = 2;
private void SetupColumns()
{
int offset = 2;
for (int i = 0; i < 81; ++i)
{
DATA.SetHeaderName(i + offset, "p" + i);
DATA.SetHeaderName(81 + i + offset, "r" + (i/9+1) + "v" + (i%9+1));
DATA.SetHeaderName(81*2 + i + offset, "c" + (i/9+1) + "v" + (i%9+1));
DATA.SetHeaderName(81*3 + i + offset, "b" + (i/9+1) + "v" + (i%9+1));
}
for (int i = 0; i < (81*4); ++i)
{
DATA.SetHeaderCount(NextNodeId, 0);
DATA.SetNodePointer(NextNodeId, 'w', (short)(NextNodeId-1));
DATA.SetNodePointer((short)(NextNodeId-1), 'e', NextNodeId);
NextNodeId += 1;
}
DATA.SetNodePointer(1, 'w', (short)(NextNodeId-1));
DATA.SetNodePointer((short)(NextNodeId-1), 'e', 1);
Debug.Assert(NextNodeId == 326, "ERROR AFTER COLUMNS NEXT NODE SHOULD BE 326, but is: " + NextNodeId);
}
public void BuildTableFull()
{
SetupColumns();
short offset = 2;
//Build the exact cover matrix
for (int index = 0; index < 81; ++index)
{
for (short CellValue = 1; CellValue <= 9; ++CellValue)
{
//AddRow(GetIndexes_SudokuConstraint(j, i));
int Row = index / 9;
int Col = index % 9;
int Block = (index / 27)*3 + (index % 9 / 3);
var columns = new [] { (index + offset),
(81*1 + (Row*9 + CellValue-1) + offset),
(81*2 + (Col*9 + CellValue-1) + offset),
(81*3 + (Block*9 + CellValue-1) + offset) };
AddRow(columns);
}
}
}
public void AddRow(params int[] row)
{
//"Add row".Dump();
short start = -1;
short prev = -1;
foreach (short cell in row)
{
short node = NextNodeId;
NextNodeId += 1;
//Columns[i].Dump("Pre", 1);
DATA.InsertNode(cell, 'n', 's', node);
DATA.SetNodePointer(node, 'h', cell);
DATA.IncreaseHeaderCount(cell);
//Columns[i].Dump("Post", 1);
if (start == -1) start = node;
if (prev != -1)
{
DATA.SetNodePointer(node, 'w', prev);
DATA.SetNodePointer(prev, 'e', node);
}
prev = node;
}
DATA.SetNodePointer(start, 'w', prev);
DATA.SetNodePointer(prev, 'e', start);
}
public void LoadSudoku(string board)
{
ADDMESSAGE("LoadSudoku");
short offset = 2;
for (int i = 0; i < 81; ++i)
{
char c = board[i];
if ('1' <= c && c <= '9')
{
short CellValue = (short)(c - '0');
short index = (short)i;
Cover((short)(index + offset));
int Row = index / 9;
int Col = index % 9;
Cover((short)(81*1 + (Row*9 + CellValue-1) + offset));
Cover((short)(81*2 + (Col*9 + CellValue-1) + offset));
int Block = (index / 27)*3 + (index % 9 / 3);
Cover((short)(81*3 + (Block*9 + CellValue-1) + offset));
DATA.PushToSolutionSet(index, CellValue);
}
}
ENDMESSAGE("LoadSudoku");
}
[Conditional("STACKON")]
public void DumpNodeIds()
{
DumpHeaderCounts();
" ==== ALL NODES ==== ".Dump();
foreach (var node in AllNodes())
{
("NodeId:#" + node.ToString()).Dump();
}
" ==== ALL NODES ==== ".Dump();
}
public void DumpHeaderCounts()
{
" ==== HEADERS ==== ".Dump();
var column = DATA.GetNodePointer(HEAD, 'e');
while (column != HEAD)
{
("Count-#" + column
+ "-" + DATA.GetHeaderCount(column)
+ "-" + DATA.GetHeaderName(column)).Dump();
column = DATA.GetNodePointer(column, 'e');
}
" ==== HEADERS ==== ".Dump();
}
private IEnumerable<short> AllNodes()
{
var column = DATA.GetNodePointer(HEAD, 'e');
while (column != HEAD)
{
yield return column;
column = DATA.GetNodePointer(column, 'e');
}
column = DATA.GetNodePointer(HEAD, 'e');
while (column != HEAD)
{
var southPointer = DATA.GetNodePointer(column, 's');
while (southPointer != column)
{
yield return southPointer;
southPointer = DATA.GetNodePointer(southPointer, 's');
}
column = DATA.GetNodePointer(column, 'e');
}
}
public void TEST_DataStore()
{
DATA.SetNodePointer(1, 'h', 2);
DATA.SetNodePointer(1, 'n', 3);
DATA.SetNodePointer(1, 's', 4);
DATA.SetNodePointer(1, 'e', 5);
DATA.SetNodePointer(1, 'w', 6);
//DATA.SetHeaderCount(1, 43);
Debug.Assert(DATA.GetNodePointer(1, 'h') == 2, "h pointer");
Debug.Assert(DATA.GetNodePointer(1, 'n') == 3, "n pointer");
Debug.Assert(DATA.GetNodePointer(1, 's') == 4, "s pointer");
Debug.Assert(DATA.GetNodePointer(1, 'e') == 5, "e pointer");
Debug.Assert(DATA.GetNodePointer(1, 'w') == 6, "w pointer");
Debug.Assert(DATA.GetHeaderCount(1) == 43, "count");
DATA.OutputStorageView();
}
public void TEST_Output()
{
DATA.OutputStorageView();
}
}
public interface IDataStores
{
short GetNodePointer(short nodeId, char direction);
void SetNodePointer(short nodeId, char direction, short pointer);
void InsertNode(short nodeId, char direction, char oppositeDir, short pointer);
short GetHeaderCount(int column);
void SetHeaderCount(int column, short count);
void IncreaseHeaderCount(int column);
void DecreaseHeaderCount(int column);
string GetHeaderName(int column);
void SetHeaderName(int column, string name);
void PushToSolutionSet(short nodeId);
void PushToSolutionSet(short index, short CellValue);
void PopFromSolutionSet();
string OutputSolutionSet();
void LoadFromFile(string filename);
void OutputStorageView();
string GetNodeString(short column);
}
public class ArrayDataStore : IDataStores
{
public ArrayDataStore()
{ }
public ArrayDataStore(string filename, string columnsFilename)
{
LoadFromFile(filename);
LoadHeadersFromFile(columnsFilename);
}
MircBvar bvar = new MircBvar();
MircHashTable hash = new MircHashTable();
string SolutionSet = "";
public short GetNodePointer(short nodeId, char direction)
{
int ptr = GetBinaryPos(nodeId, direction);
return new ShortUnion(bvar.bvar(ptr, ptr+1)).Short;
}
public void SetNodePointer(short nodeId, char direction, short pointer)
{
int ptr = GetBinaryPos(nodeId, direction);
bvar.bset(ptr, new ShortUnion(pointer).Array);
}
public void InsertNode(short nodeId, char direction, char oppositeDir, short node)
{
SetNodePointer(node, oppositeDir, nodeId);
short ptr = GetNodePointer(nodeId, direction);
if (ptr == 0) ptr = nodeId;
SetNodePointer(ptr, oppositeDir, node);
SetNodePointer(node, direction, ptr);
SetNodePointer(nodeId, direction, node);
// setOpposite(node, this);
// setOpposite(getPosition(this) ?? this, node);
// setPosition(node, getPosition(this) ?? this);
// setPosition(this, node);
}
private int GetBinaryPos(short nodeId, char direction)
{
int ptr = (nodeId - 1) * 2 * 5;
switch (direction)
{
case 'h':
return ptr;
case 'n':
return ptr + 2;
case 's':
return ptr + 4;
case 'e':
return ptr + 6;
case 'w':
return ptr + 8;
}
return -1;
}
//Using a hash table for the column properties
public short GetHeaderCount(int column)
{
return short.Parse(hash.hget(column + "count"));
}
public void SetHeaderCount(int column, short count)
{
hash.hset(column + "count", count.ToString());
}
public void IncreaseHeaderCount(int column)
{
short count = (short)(GetHeaderCount(column) + 1);
hash.hset(column + "count", count.ToString());
}
public void DecreaseHeaderCount(int column)
{
short count = (short)(GetHeaderCount(column) - 1);
hash.hset(column + "count", count.ToString());
}
public string GetHeaderName(int column)
{
return hash.hget(column + "header");
}
public void SetHeaderName(int column, string name)
{
hash.hset(column + "header", name);
}
public void PushToSolutionSet(short nodeId)
{
SolutionSet += " " + nodeId;
}
public void PopFromSolutionSet()
{
SolutionSet = SolutionSet.Substring(0, SolutionSet.LastIndexOf(' '));
}
Dictionary<string, string> outputCache = new Dictionary<string, string>();
public void PushToSolutionSet(short index, short CellValue)
{
outputCache.Add(index.ToString(), CellValue.ToString());
}
public string OutputSolutionSet()
{
//foreach token in SolutionSet
//get header, get name, check if "^p"
//If does, parse number out of that, that is the cell index
// then go west from cell, and get the name of that, and parse out 'v(\d)$'
//If it doesn't, go west from the cell, and repeat options
//Construct this into a string somehow
//maybe throw everything in a hash table, then read out of that by cell number
//Or could use a small bvar, since i can set those values by position!!
foreach (string s in SolutionSet.Split(new [] {' '}, StringSplitOptions.RemoveEmptyEntries))
{
bool notFound = true;
short atPointer = short.Parse(s);
string position = null;
string value = null;
while (notFound)
{
var header = GetNodePointer(atPointer, 'h');
string name = GetHeaderName(header);
var m = Regex.Match(name, @"^[prcb](\d+)(v(\d)|)$");
if (m.Groups[2].Value == "")
{
position = m.Groups[1].Value;
}
else
{
value = m.Groups[3].Value;
}
notFound = position == null || value == null;
atPointer = GetNodePointer(atPointer, 'w');
}
outputCache.Add(position, value);
}
string output = "";
for (int i = 0; i < 81; ++i)
{
output += outputCache[i.ToString()];
}
return output;
}
public void LoadFromFile(string filename)
{
bvar.SetByteArray(File.ReadAllBytes(filename));
}
public void LoadHeadersFromFile(string filename)
{
//TODO THIS IS NOT CONVERTABLE
var lines = File.ReadAllLines(filename);
for (int i = 0; i < lines.Length; ++i)
{
if (string.IsNullOrEmpty(lines[i])) continue;
string[] sa = lines[i].Split('\t');
SetHeaderName(i+2, sa[0]);
SetHeaderCount(i+2, short.Parse(sa[1]));
}
}
public void OutputStorageView()
{
hash.OutputStorageView();
bvar.OutputStorageView();
}
public string GetNodeString(short column)
{
return "Id: " + column + " "
+ "h" + GetNodePointer(column, 'h').ToString("0000 ")
+ "n" + GetNodePointer(column, 'n').ToString("0000 ")
+ "s" + GetNodePointer(column, 's').ToString("0000 ")
+ "e" + GetNodePointer(column, 'e').ToString("0000 ")
+ "w" + GetNodePointer(column, 'w').ToString("0000 ");
}
[StructLayout(LayoutKind.Explicit)]
struct ShortUnion
{
public ShortUnion(short value)
{
byte0 = 0;
byte1 = 0;
Short = value;
}
public ShortUnion(byte[] array)
{
Short = 0;
byte0 = array[0];
byte1 = array[1];
}
[FieldOffset(0)]
public byte byte0;
[FieldOffset(1)]
public byte byte1;
[FieldOffset(0)]
public short Short;
public byte[] Array
{
get
{
return new byte[] { byte0, byte1 };
}
set
{
byte0 = value[0];
byte1 = value[1];
}
}
}
}
public class MircBvar
{
byte[] Storage = new byte[1024*32];
public void bset(int address, byte[] values)
{
for (int i = 0; i < values.Length; ++i)
{
Storage[address+i] = values[i];
}
}
public byte[] bvar(string range)
{
if (!range.Contains('-'))
return bvar(Int32.Parse(range));
if (range.EndsWith("-"))
return bvar(Int32.Parse(range.Substring(0, range.Length-1)));
var m = Regex.Match(range, @"^(\d)-(\d)$");
return bvar(Int32.Parse(m.Groups[1].Value), Int32.Parse(m.Groups[2].Value));
}
public byte[] bvar(int pos)
{
return bvar(pos, pos);
}
public byte[] bvar(int start, int end)
{
//start = start-1;
int len = end-start+1;
var output = new byte[len];
for (int i = 0; i < len; ++i)
{
output[i] = Storage[start + i];
}
return output;
}
public void SetByteArray(byte[] array)
{
Storage = array;
array.Length.Dump();
}
public void OutputStorageView()
{
BitConverter.ToString(Storage).Replace("-", "").Dump();
}
}
//public class HashTableDataStore : IDataStores
//{
//}
public class MircHashTable
{
Dictionary<string, string> Storage = new Dictionary<string, string>();
public void hset(string key, string value)
{
Storage[key] = value;
}
public string hget(string key)
{
return Storage[key];
}
public void OutputStorageView()
{
Storage.Dump();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment