// x 0..MaxX, y 0..MaxY. // Выполняет проверку на сумму CRC если установлен флаг check: // возвращает код ошибки и не считает дальше. Если проверка не пройдена, то // функция удаляет из всех использованных сумм домино d. Таким образом, // после вызова функции, которая возвращает код ошибки > 0, можно продолжать проверку, // как будто домино d не было учтено в суммах. // Коды ошибок: // 0. Все проверки успешны. // 1. Нет смысла поворачивать костяшку. // 2. Можно и нужно повернуть костяшку. public byte AddDomino(byte x, byte y, ref Domino d) { // вначале заполняем вертикальный ряд, он заполняется быстрее, чем строка, поэтому проверяем сумму по вертикали первой. это позволяет нам быстрее отбрасывать ненужные варианты. tmpX = (byte)(sumX[x] + d.sum); if (y == brdY) { if (tmpX != CRC) return 1; // нет смысла поворачивать. в любом случае не сойдется. } else if (tmpX > CRC) return 1; // нет смысла поворачивать. в любом случае не сойдется. // потом заполняем строку, проверяем ее второй. byte yt = (byte)(y + y); byte yb = (byte)(yt + 1); tmpYt = (byte)(sumY[yt] + d.top); if (x == brdX) // строка заполнена { if (tmpYt != CRC) if (d.IsNotSimmetric) // проверяем сойдется ли сумма верхнего ряда чисел и нижнего, если перевернуть костяшку (экономим на повороте в главном цикле; это занимает больше времени чем проверка здесь) if (sumY[yt] + d.bottom == CRC && sumY[yb] + d.top == CRC) return 2; // повернуть можно else return 1; // повернуть бессмысленно else return 1; // повернуть бессмысленно } else // строка не заполнена if (tmpYt > CRC) if (d.IsNotSimmetric) if (sumY[yt] + d.bottom > CRC || sumY[yb] + d.top > CRC) return 1; // повернуть бессмысленно else return 2; // повернуть можно else return 1; // повернуть бессмысленно tmpYb = (byte)(sumY[yb] + d.bottom); if (x == brdX) { if (tmpYb != CRC) if (d.IsNotSimmetric) if (sumY[yt] + d.bottom == CRC && sumY[yb] + d.top == CRC) return 2; // повернуть можно else return 1; else return 1; // повернуть бессмысленно } else if (tmpYb > CRC) if (d.IsNotSimmetric) if (sumY[yt] + d.bottom > CRC || sumY[yb] + d.top > CRC) return 1; // повернуть бессмысленно else return 2; // повернуть можно else return 1; // повернуть бессмысленно // диагональ снизу вверх заполняется раньше чем диагональ сверху вниз (она заполниться когда мы поставим // костяшку (x=brX;y=0)), поэтому проверяем ее вначале. так можно отсеять быстрее неправильные варианты. bool changedDU = false; if (x == brdX - yt) // верхняя часть костяшки на DU диагонали { tmpDU = (byte)(DU + d.top); if (y == 0) // вся диагональ DU заполнена { if (tmpDU != CRC) if (d.IsNotSimmetric) if (DU + d.bottom != CRC) return 1; // повернуть бессмысленно else return 2; // повернуть можно else return 1; // повернуть бессмысленно } else if (tmpDU > CRC) if (d.IsNotSimmetric) if (DU + d.bottom > CRC) return 1; // повернуть бессмысленно else return 2; // повернуть можно else return 1; // повернуть бессмысленно changedDU = true; } else if (x == brdX - yb) // нижняя часть костяшки на DU диагонали { tmpDU = (byte)(DU + d.bottom); if (tmpDU > CRC) // здесь никогда не будет заполнена полностью диагональ DU if (d.IsNotSimmetric) if (DU + d.top > CRC) return 1; // повернуть бессмысленно else return 2; // повернуть можно else return 1; // повернуть бессмысленно changedDU = true; } bool changedUD = false; if (x == yt) // верхняя часть костяшки на UD диагонали { tmpUD = (byte)(UD + d.top); if (tmpUD > CRC) // никогда не будет здесь заполнена вся диагональ UD if (d.IsNotSimmetric) if (UD + d.bottom > CRC) return 1; // повернуть бессмысленно else return 2; // повернуть можно else return 1; // повернуть бессмысленно changedUD = true; } else if (x == yb) // нижняя часть костяшки на UD диагонали { tmpUD = (byte)(UD + d.bottom); if (x == brdX) // заполнана вся диагональ UD { if (tmpUD != CRC) if (d.IsNotSimmetric) if (UD + d.top != CRC) return 1; // повернуть бессмысленно else return 2; // повернуть можно else return 1; // повернуть бессмысленно } else if (tmpUD > CRC) if (d.IsNotSimmetric) if (UD + d.top > CRC) return 1; // повернуть бессмысленно else return 2; // повернуть можно else return 1; // повернуть бессмысленно changedUD = true; } // Только если прошли все проверки записываем временные данные в суммы sumX[x] = tmpX; sumY[yt] = tmpYt; sumY[yb] = tmpYb; if (changedDU) DU = tmpDU; if (changedUD) UD = tmpUD; return 0; }
// Найти такую таблицу из домино (MaxY строки в MaxY*2 рядов), что суммы по вертикали, горизонтали и главным диагоналям будет == CRC. public void Solve() { if(MaxI == 0) return; // максимальный номер костяшки домино: 0 - (0;0), 1 - (0;1), ... 27 - (6;6). int iRefresh = 0; // итератор для определения, когда выводить промежуточную информацию UInt64 count = 0; // количество итераций int NumSolv = 0; // количество найденных решений // n - текущая обрабатываемая позиция в таблице == x + y*MaxX // Кроме позиции домино n (0..MaxN-1) используется ориентация костяшек false, true (см. Domino.rot) byte n = 0; byte x = 0; byte y = 0; //текущий номер домино, вставляемый в позицию n слова. Изменяется от 0...(MaxI-1). byte i = 0; //отмечаем уже используемые домино bool[] used = new bool[MaxI]; Domino[] d = new Domino[MaxN]; // массив расставленных домино, их использовать нельзя // разделитель строк костяшек домино. для отрисовки на экране. String divline = ""; for (byte t = 0; t < MaxX; t++) divline += "- "; Summs Summs1 = new Summs(MaxX,MaxY, CRC); // объект для подсчета контрольных сумм по рядам, строкам и главным диагоналям. byte code = 0; // код проверки CRC. если 0 - то все ок и в Summs1 учтена текущая костяшка. иначе в Summs1 нет текущей костяшки (удалять ее из суммы не нужно). NotSolve = true; // нет решения. DateTime StartTime = DateTime.Now; String threadName = "Поток " + startI.ToString("D2") + ":" + endI.ToString("D2") + "| "; { // Отладочная информация String str = Environment.NewLine + threadName + "Число домино: " + MaxI.ToString() + Environment.NewLine; str += threadName + "Начинаем обработку " + StartTime.ToString("yyyy.MM.dd hh:mm:ss-ff") + Environment.NewLine; SetTableText(str); //SetInfo(""); } bool NotNull = false; // есть ли значение в d[n] DateTime Now = DateTime.Now; // Устанавливаем начальное значение i = startI; // В цикле пока не найдем решение. while (true) { count++; if (iRefresh == RefreshTo) { // выводим диагностику iRefresh = 0; String debugtxt = threadName + (DateTime.Now - Now).TotalMilliseconds.ToString("00000000") + " "; debugtxt += count.ToString("D12") + ": "; for (byte tn = 0; tn < MaxN && d[tn] != null; tn++) { debugtxt += d[tn].ToString() + "|"; } debugtxt += Environment.NewLine; SetTableText(debugtxt); Now = DateTime.Now; } else iRefresh++; // Обрабатываем костяшку в позиции n: либо поворачиваем костяшку, если она уже установлена, либо ищем подходящую из свободных. // подбираем первый подходящий номер i if (NotNull && code != 1 && d[n].IsNotSimmetric && !d[n].rot) { if(code == 0) Summs1.RemoveDomino(x, y, ref d[n]); // удаляем из контрольных сумм, данные текущей костяшки. d[n].Rotate(); } else { if (NotNull) // На этом месте уже стоит повернутая костяшка. Убираем ее. { if (code == 0) Summs1.RemoveDomino(x, y, ref d[n]); // удаляем из контрольных сумм, данные текущей костяшки. if (d[n].rot) d[n].Rotate(); // она повернута, вернем в исходное состояние. i = d[n].num; // запоиминаем номер убираемой костяшки. used[i] = false; // отмечаем что убираемая костяшка не используется. d[n] = null; // очищаем позицию домино. NotNull = false; // в текущей позиции d[n] ничего нет i++; // была костяшка номер num. ищем слещующую по номеру не использованную. } // ищем следующую свободную костяшку while (i < MaxI && used[i]) i++; if ((n > 0 && i < MaxI) || (n == 0 && i < endI)) // граница перебора костяшек, либо до endI в первой позиции, либо любая костяшка { //помещаем костяшку с номером i в текущую позицию n d[n] = NumDomino[i]; NotNull = true; // в текущей позиции d[n] установлена костяшка used[i] = true; } else // смещаемся на предыдущую позицию, т.к. эту заполнить не получилось. { if ((endI == 0 && n == 0) || (endI > 0 && n == 1)) // здесь еще не сместились, но считаем, что сместились. { Now = DateTime.Now; SetInfo(threadName + "Обработка закончена"); SetTableText(threadName + " Тек. время: " + Now.ToString("yyyy.MM.dd hh:mm:ss-ff") + ". Прошло млсек.: " + (Now - StartTime).TotalMilliseconds.ToString("00000000") + Environment.NewLine); break; // перебрали все возможные варианты, заканчиваем обработку. } n--; if (y == 0) // дальше смещаться уже некуда { x--; y = (byte)(MaxY - 1); } else y--; i = 0; code = 0; // чтобы удалялись из контрльных сумм костяшки в предыдущих позициях. NotNull = true; // в текущей позиции d[n] установлена костяшка continue; // заходим на проверку новых перестановок в предыдущей позиции. } } // Здесь у нас в позиции n == (x,y) есть новая костяшка (или ее позиция) d[n]. // Проверяем условия сумм по горизонтали, вертикали и диагоналям code = Summs1.AddDomino(x, y, ref d[n]); // рассчитываем суммы и проверяем их if (code > 0) // не прошли по контрольной сумме. из контрольных сумм костяшка исключена! { //if (code == 1 && !d[n].rot && d[n].IsNotSimmetric) // d[n].Rotate(); // проскакиваем итерацию цикла с поворотом костяшки. continue; // здесь code обнулять не нужно. на следущей итерации цикла поиска, данная костяшка не будет исключена из контрльных сумм, так как это уже сделано. } // Позиция n заполнена подходящей домино. Переходим к следующей позиции. n++; y++; if(y >= MaxY) { y = 0; x++; } i = 0; NotNull = false; // в следующей позиции не стоит костяшка. if (n == MaxN) // конец слова: все домино расставлены { // отображаем слово NotSolve = false; NumSolv++; Now = DateTime.Now; String text = threadName + "!!! Нашли решение №" + NumSolv.ToString() + Environment.NewLine; text += "Номер итерации " + count.ToString() + Environment.NewLine; text += "Тек. время: " + Now.ToString("yyyy.MM.dd-hh:mm:ss-ff") + ". Прошло млсек.: " + (Now - StartTime).TotalMilliseconds.ToString("00000000") + Environment.NewLine; for (byte tempy = 0; tempy < MaxY; tempy++) { String nextstr = ""; for (byte tempx = 0; tempx < MaxX; tempx++) { byte tempn = (byte)(tempy + tempx * MaxY); text += d[tempn].top.ToString() + "|"; nextstr += d[tempn].bottom.ToString() + "|"; } text += Environment.NewLine + nextstr; text += Environment.NewLine + divline + Environment.NewLine; } text += Environment.NewLine + Environment.NewLine; SetTableText(text); // смещаемся на предыдущую позицию и ищем следущюую позицию домино n--; if (y == 0) // смещаться уже некуда { x--; y = (byte)(MaxY - 1); } else y--; NotNull = true; // в предыдущей позиции стоит костяшка. } } // Отображаем информацию о решении. SetTableText(Environment.NewLine + threadName + "Количество решений " + NumSolv.ToString() + Environment.NewLine + threadName + "Количество итераций " + count.ToString()); }
public void RemoveDomino(byte x, byte y, ref Domino d) { byte yt = (byte)(y + y); // вертикальная координата верхнего числа костяшки в таблице чисел (не костяшек!) byte yb = (byte)(yt + 1); // вертикальная координата нижнего числа костяшки в таблице чисел (не костяшек!) sumX[x] -= d.sum; // убираем сумму костяшки из вертикального ряда по х sumY[yt] -= d.top; // убираем верхнее число костяшки из верхнего горизонтального ряда чисел sumY[yb] -= d.bottom; // убираем нижнее число костяшки из нижнего горизонтального ряда чисел // убираем сумму из главных диагоналей if (x == yt) UD -= d.top; // верхняя часть костяшки лежит на диагонали сверху вниз else if (x == yb) UD -= d.bottom; // нижнаяя часть костяшки лежит на диагонали сверху вниз if (x == brdX - yt) DU -= d.top; // верхняя часть костяшки лежит на диагонали снизу вверх else if (x == brdX - yb) DU -= d.bottom; // нижняя часть костяшки лежит на диагонали снизу вверх }
byte startI; // начальный код костяшки домино на позиции (0,0). С нее начинается проверка. Нужно для распараллеливания процесса. #endregion Fields #region Constructors public DominoTable(byte MaxY, byte CRC, byte startI, byte endI, SetText SetInfo, SetText SetTableText, int RefreshTo = 1000000000) { this.MaxY = MaxY; this.MaxX = (byte)(MaxY * 2); this.MaxN = (byte)(MaxX * MaxY); this.CRC = CRC; this.startI = startI; this.RefreshTo = RefreshTo; this.SetInfo = SetInfo; this.SetTableText = SetTableText; NotSolve = true; NumDomino = new Domino[28]; // Больше чем 28 костяшек домино от (0;0) до (6;6) нет. byte num = 0; for(byte top = 0; top < 7; top++) { for (byte bottom = top; bottom < 7; bottom++) { //if (top + bottom > 8) continue; // нам не подойдут домино с суммой чисел больше 8 NumDomino[num] = new Domino(top, bottom, num); num++; } } MaxI = num; if (endI == 0) this.endI = MaxI; // определяем если нужно максимальное значение else this.endI = endI; }
// То же что и AddDomino, но без проверки public void AddDominoWithoutCheck(byte x, byte y, ref Domino d) { // вначале заполняем вертикальный ряд, он заполняется быстрее, чем строка, поэтому проверяем сумму по вертикали первой. это позволяет нам быстрее отбрасывать ненужные варианты. sumX[x] += d.sum; // потом заполняем строку, проверяем ее второй. byte yt = (byte)(y * 2); byte yb = (byte)(yt + 1); sumY[yt] += d.top; sumY[yb] += d.bottom; // костяшку (x=brX;y=0)), поэтому проверяем ее вначале. так можно отсеять быстрее неправильные варианты. if (x == brdX - yt) // верхняя часть костяшки на DU диагонали DU += d.top; else if (x == brdX - yb) // нижняя часть костяшки на DU диагонали DU += d.bottom; if (x == yt) // верхняя часть костяшки на UD диагонали UD += d.top; else if (x == yb) // нижняя часть костяшки на UD диагонали UD += d.bottom; }