2010-05-15 8 views
1

Я пытаюсь создать игру для судоку, для тех, кто не знает, что это такое. У вас есть поле 9x9, которое должно быть заполнено цифрами от 1 до 9, каждое число должно быть уникальным в его строке и столбце, а также в поле 3x3 оно найдено. Я закончил работу с множеством циклов в двухмерном массиве.Вложенные петли неожиданно вспыхивают

Но в какой-то момент он просто останавливается, без каких-либо исключений, просто вырывается, и ничего не происходит, и это не всегда в одном положении, но всегда идет на полпути.

Я ожидал исключения переполнения стека, по крайней мере.

Вот мой код:

public class Engine 
{ 
    public int[,] Create() 
    { 
     int[,] outer = new int[9, 9]; 


     for (int i = 0; i < 9; i++) 
     { 
      for (int j = 0; j < 9; j++) 
      { 
       outer[i, j] = GetRandom(GetUsed(outer, i, j)); 
      } 
     } 

     return outer; 

    } 

    List<int> GetUsed(int[,] arr, int x, int y) 
    { 
     List<int> usedNums = new List<int>(); 
     for (int i = 0; i < 9; i++) 
     { 
      if (arr[x, i] != 0 && i != y) 
      { 
       if(!usedNums.Contains(arr[x, i])) 
        usedNums.Add(arr[x, i]); 
      } 
     } 

     for (int i = 0; i < 9; i++) 
     { 
      if (arr[i, y] != 0 && i != x) 
      { 
       if (!usedNums.Contains(arr[i, y])) 
        usedNums.Add(arr[i, y]); 
      } 
     } 

     int x2 = 9 - (x + 1); 
     int y2 = 9 - (y + 1); 

     if (x2 <= 3) 
      x2 = 2; 
     else if (x2 > 3 && x2 <= 6) 
      x2 = 5; 
     else x2 = 8; 

     if (y2 <= 3) 
      y2 = 2; 
     else if (y2 > 3 && y2 <= 6) 
      y2 = 5; 
     else y2 = 8; 

     for (int i = x2 - 2; i < x2; i++) 
     { 
      for (int j = y2 - 2; j < y2; j++) 
      { 
       if (arr[i, j] != 0 && i != x && j != y) 
       { 
        if (!usedNums.Contains(arr[i, j])) 
         usedNums.Add(arr[i, j]); 
       } 
      } 
     } 

     return usedNums; 
    } 

    int GetRandom(List<int> numbers) 
    { 
     Random r; 
     int newNum; 
     do 
     { 
      r = new Random(); 
      newNum = r.Next(1, 10); 
     } while (numbers.Contains(newNum)); 

     return newNum; 
    } 

} 
+0

Вы пробовали пройтись и посмотреть, где он остановился? Кроме того, о какой функции мы говорим? Там есть полдюжины петель. – tzaman

+0

Я перехожу через первый цикл в первом методе «Create()» Это никогда не бывает так, иногда в 4,7 или 5,3, всегда в другом положении, это кажется очень случайным. –

+0

Очень ... * случайный * вы говорите? (Я не удержался) – Phil

ответ

3

Это не вспыхивают, это застрять в бесконечном цикле.

Если я читаю это право, похоже, вы пытаетесь создать доску для судоку, на которой можно играть. Проблема в том, что это не так просто, как вы думаете. Насколько я вижу, вы просто просматриваете его, заполняя его случайными неиспользованными значениями; проблема в том, что не каждая такая конфигурация будет действительной - в большинстве случаев ваши случайные записи в конечном итоге помещают вас в неразрешимую конфигурацию платы.

Затем, когда вы пытаетесь выбрать случайное значение для некоторого квадрата, внутренняя GetRandom функция будет просто цикл бесконечно пытается не выбрать номер (с getUsed уже есть все 1 - 9, то do..while будет никогда выход).

Легкий способ увидеть это для себя: добавить это в верхней части GetRandom функции:

if (Enumerable.Range(1, 9).All(i => numbers.Contains(i))) 
    Console.WriteLine("PROBLEM!"); 

Теперь, если numbers имеет все 1 до 9, то вам скажу. (И затем продолжайте застревать в бесконечном цикле, но это ваша проблема сейчас;))

Также, как и в случае с сайтом, не рекомендуется делать new Random() объектов; лучше просто иметь один экземпляр Random, инициализировать его в своем конструкторе, а затем продолжать использовать Random.Next. По крайней мере, у вас есть Random r = new Random() в верхней части функции, а затем просто запустите r.Next.

Хорошо, вот очень простой пример получения в неразрешимую позиции, только в первых двух строках:

123|456|789 
456|123|X 

Там нет действительного номера не осталось поставить в положение, обозначенное X - посмотреть, как это случилось?
Заполнение сетки случайными неиспользуемыми номерами - это не то же самое, что заполнять ее «ответами» - подумайте об этом таким образом, если вы взяли обычную игру судоку и попытались ответить , решите, просто положив произвольное число, которое выполнено правила на каждом пустом квадрате - вы скоро застрянете, не так ли? Это именно то, что происходит с вашей программой.

Я предлагаю вам сначала написать sudoku solver, который берет действительную начальную конфигурацию платы и пытается ее решить - тогда вы можете перейти к попытке сделать доски самостоятельно.

+0

+1, отметив, что этот способ создания платы приведет к неразрешимым/множественным советам решений. – Phil

+0

Ну сначала я пытаюсь заполнить его ответами, тогда я собираюсь использовать его и удалить некоторые из значений. Как это может быть неразрешимым или с несколькими решениями, когда числа уникальны в пределах их строки, столбца и окна 3x3? –

+0

См. Отредактированный ответ в ответе. – tzaman

1

Что произойдет, если в GetRandom, ваш список numbers был заполнен 1-10?

Я думаю, что ваша программа замерзает в методе GetRandom, потому что вы говорите ему, чтобы он бесконечно вращается, пока не найдет число от 1 до 10, которого нет в списке.

Вы должны сказать ему, чтобы он искал от 0 до 9, считая, что 0 является «пустым», и позволяет ему уйти, если он получает 0, потому что на плате только 1-9. По умолчанию он окажется в списке по умолчанию, поэтому просто игнорируйте его.

do 
{ 
    r = new Random(); 
    newNum = r.Next(0, 9); 
} while (numbers.Contains(newNum) && newNum != 0); 

Дайте этот снимок и посмотрите, работает ли он!

+0

MaxValue не следует включать в диапазон, пока значение minValue. Я пробовал уже на всякий случай, и он заполнил мой массив 0s. То, что я не понимаю, так это то, как это может происходить в бесконечном цикле. Как я вижу это, он заполняет список чисел, которые уже используются в строке, столбце и блоке ячейки, поэтому метод не возвращает его, на последней итерации внутреннего цикла он должен иметь один номер из 1- 9 не используется, или я чего-то не хватает? Обратите внимание, что я не пытаюсь подвергать сомнению ваши рассуждения. –

+0

Вы попробовали предложение @ tzaman по поводу проверки чека в верхней части 'GetRandom'?Ваш код не собирает список чисел всего за 1 ячейку (которая всегда будет ровно 1 номером). Он собирает список номеров, используемых в строке строк, столбцов и 3х3. В первой строке проблем не будет. После этого все ставки отключены. Последний элемент второй строки может иметь проблему, так как есть 8 ячеек до, 1 выше и 1 в верхнем левом углу. Это все 10 номеров, которые вы разрешаете в «Random.Next». – Phil

+0

Я напечатал номера, которые ему удалось создать, и заметил проблему с этим. –

1

я щипнул GetRandom для выполнения

static int GetRandom(List<int> usedNums) 
{ 
    List<int> missingNums = new List<int>(); 
    for (int i = 1; i <= 10; i++) 
    { 
     if (!usedNums.Contains(i)) 
      missingNums.Add(i); 
    } 

    Random r = new Random(); 
    int rMissingNumIndex = r.Next(0, missingNums.Count - 1); 

    return missingNums[rMissingNumIndex]; 
} 

и получил исключение, потому что usedNums был принят с 10 элементами, что означает, что не может быть никакого решения. Попробуйте использовать этот метод, и вы увидите проблему.

+0

Не означает ли это, что проблема связана с функцией GetUsed или? –

Смежные вопросы