/// <summary> /// Попытка хода игрока /// </summary> /// <param name="newCellValue">Ячейка с новым значением</param> /// <param name="winnerId">Если в результате этого хода определился игрок, возвращаем его Id</param> /// <returns>true, если этот ход может быть совершен, иначе false</returns> public bool TrySetValue(CSudokuCell newCellValue, out int?winnerId) { winnerId = null; if (newCellValue.X < 0 || newCellValue.X > cellGrid.GetUpperBound(0) || newCellValue.Y < 0 || newCellValue.Y > cellGrid.GetUpperBound(1)) { return(false); } // Определение в серверной таблице изменяемой ячейки по координатам CSudokuCell serverCell = cellGrid[newCellValue.X, newCellValue.Y]; if (serverCell.IsTask) { // Если эта ячейка является условием, игнорируем запрос на её изменение return(false); } // Нельзя допустить, чтобы два хода произошли одновременно lock (cellGrid) { // Можно поставить значение в ошибочную ячейку, или в пустую или ту, в которой значение было установлено этим же игроком, в противном случае ход не происходит if (serverCell.Error || null == serverCell.Owner || newCellValue.Owner == serverCell.Owner) { if (newCellValue.Value == 0) { // Если игрок стирает значение в ячейке, она вновь становится "ничьей" serverCell.Owner = null; serverCell.Value = 0; } else { // В ячейку поставлено реальное значение, она помечается занятой этим игроком serverCell.Owner = newCellValue.Owner; serverCell.Value = newCellValue.Value; if (TestForWin()) { // Игра окончена, победитель определён winnerId = newCellValue.Owner; } } // Пометка ошибочных ячеек: тех, которые имеют значение такое же, как и устанавливаемое значение, и находятся в той же строке, столбце или регионе. В ошибочную ячейку пожет поставить своё значение другой игрок. TestForError(serverCell); return(true); } } return(false); }
/// <summary> /// Возвращает текущее состояние игры, для передачи его игрокам. /// </summary> /// <param name="realGame">При false возвращает пустое поле. Используется для очистки поля у игрока, покинувшего турнир.</param> /// <returns>Текущее состояние игры</returns> public CSudokuState GetGameState(bool realGame = true) { CSudokuCell[] array = new CSudokuCell[cellGrid.Length]; int i = 0; foreach (CSudokuCell cell in cellGrid) { array[i++] = realGame ? cell : CSudokuCell.NULL(cell.X, cell.Y); } return(new CSudokuState { Level = realGame ? GameLevel : SudokuLevel.Unknown, Values = array }); }
/// <summary> /// Проверка нового значения в ячейки на то, что оно - ошибочно. Ошибочные ячейки помечаются. /// </summary> /// <param name="testingCell">Проверяемая ячейка</param> private void TestForError(CSudokuCell testingCell) { for (int i = 0; i < 9; i++) { // Проверка по строке if (i != testingCell.Y) { cellGrid[testingCell.X, i].TestError(testingCell); } // Проверка по столбцу if (i != testingCell.X) { cellGrid[i, testingCell.Y].TestError(testingCell); } } // Проверка по региону 3х3 // Определение "координат" региона int regX = testingCell.X / 3; int regY = testingCell.Y / 3; for (int x = 0; x < 3; x++) { // Координата Х проверяемой ячейки в регионе int realX = regX * 3 + x; for (int y = 0; y < 3; y++) { // Координата Y проверяемой ячейки в регионе int realY = regY * 3 + y; // Не сравниваем ячейку саму с собой if (realX != testingCell.X || realY != testingCell.Y) { cellGrid[realX, realY].TestError(testingCell); } } } }
/// <summary> /// Перенос сформированного условия в ячейки игры /// </summary> /// <param name="grid"></param> private void FillSudokuCells(byte[,] grid) { cellGrid = new CSudokuCell[grid.GetLength(0), grid.GetLength(1)]; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { cellGrid[i, j] = new CSudokuCell { Value = grid[i, j], X = i, Y = j, }; if (grid[i, j] > 0) { // Пометка ячейки со значением как "принадлежащей серверу". В неё не может поставить своё значение никакой игрок. cellGrid[i, j].Owner = CSudokuCell.ServerId; } } } }