Крестики-нолики без ИИ

Я делаю домашнее задание для UNI, и я должен сделать крестики-нолики без какого-либо решения игрока, все ходы выбираются случайным образом. Таким образом, если символ в матрице « », это означает, что он свободен, а если это «X» или «O», он должен генерировать другой ход. Это код (язык C):

if (playerTurn == 1){
    playerSymb = 'X';
}
else if (playerTurn == 2){
    playerSymb = 'O';
}

if (matrix[rand1][rand2] == ' '){
    matrix[rand1][rand2] = playerSymb;
} else if(matrix[rand1][rand2] == 'X' || matrix[rand1][rand2] == 'O'){
    do{
        randAlt1 = MINRND + rand()%(MAXRND - MINRND +1);
        randAlt2 = MINRND + rand()%(MAXRND - MINRND +1);
    }while (matrix[randAlt1][randAlt2] != 'X' && matrix[randAlt1][randAlt2] != 'O');
    matrix[randAlt1][randAlt2] = playerSymb;
}

Я не копировал весь код, потому что он вообще не закончен, мне просто нужна помощь в решении этого. Но если я попытаюсь запустить это, символы могут быть перезаписаны, например, если у меня есть «X» на matrix[1][2], возможно, что через несколько ходов это будет «O». Итак, как я могу сделать так, чтобы ходы не перезаписывались? (Извините за плохой английский).


person kHz-    schedule 17.11.2015    source источник
comment
while должен проверить на != ' '   -  person Paul Ogilvie    schedule 17.11.2015
comment
matrix[randAlt1][randAlt2] != 'X' && matrix[randAlt1][randAlt2] != 'O' Это условие неверно.   -  person Kotshi    schedule 17.11.2015


Ответы (2)


Просто поставьте правильное условие:

while (matrix[randAlt1][randAlt2] == 'X' || matrix[randAlt1][randAlt2] == 'O')

(т.е. попробуйте еще раз, если эта ячейка не пуста)

Также легко упростить код, ничего не теряя:

randAlt1 = rand1;
randAlt2 = rand2;
while (matrix[randAlt1][randAlt2] != ' ') {
    randAlt1 = MINRND + rand()%(MAXRND - MINRND +1);
    randAlt2 = MINRND + rand()%(MAXRND - MINRND +1);
}
matrix[randAlt1][randAlt2] = (playerTurn == 1) ? 'X' : 'O';

А еще лучше добавить защиту от петель, чтобы предотвратить бесконечный цикл (или добавить специальные проверки на этот случай):

randAlt1 = rand1;
randAlt2 = rand2;
int nbAttempts = 0;
while (matrix[randAlt1][randAlt2] != ' ' && nbAttempts < 100) {
    randAlt1 = MINRND + rand()%(MAXRND - MINRND +1);
    randAlt2 = MINRND + rand()%(MAXRND - MINRND +1);
    nbAttempts++;
}
if (matrix[randAlt1][randAlt2] != ' ') {
    // show error message and stop the game
}
matrix[randAlt1][randAlt2] = (playerTurn == 1) ? 'X' : 'O';
person Ilya    schedule 17.11.2015
comment
если я поставлю это условие, в какой-то момент он перестанет генерировать символы. - person kHz-; 17.11.2015
comment
Я попробую последнее условие, которое вы написали, TY. Но я не могу использовать оператор ? : потому что мои учителя говорят, что это очень-очень плохо. - person kHz-; 17.11.2015
comment
он перестанет генерировать символы? О чем ты говоришь? Хорошо, не обязательно использовать тернарный оператор (но вы видите, что моя программа в два раза короче и проще). - person Ilya; 17.11.2015
comment
Я имею в виду, что если я поставлю условие с логическим ИЛИ ( || ), оно перестанет «генерировать» ходы в какой-то момент, например, некоторые поля останутся символом «». Но условие != ' ' работает нормально, ты! - person kHz-; 17.11.2015

Вы выбираете произвольную позицию, а затем проверяете, свободна ли она — возможно, несколько раз. Но вы также можете выбрать номер свободной позиции, а затем найти ее.

Сначала настройте счетчик ходов

int   turnNo = 0;

затем сделайте цикл для альтернативных ходов, который выбирает одну из 9-turnNo неиспользуемых позиций, находит ее, помечает меткой игрока и проверяет, сделал ли ход линию из трех:

while(turnNo < 9)
{
    char  currPlayerMark = ...choose 'X' or 'O';

    int   freePos = 9 - turnNo;
    int   currPos = rand() % freePos;  // 0 .. freePos-1

    for(x=0; x<3; x++)
    {
        for(y=0; y<3; y++)
        {
            if(matrix[x][y] == ' ')  // a free position
                if(--currPos < 0)    // the sought one
                    break;           // break the inner loop
        }
        if(currPos < 0)
            break;                   // break the outer loop
    }

    matrix[x][y] = currPlayerMark;

    if(test_for_win_position(x,y))
    {
         message_a_win_of_player(currPlayerMark);
         break;  // turnNo < 9 here
    }

    turnNo ++;
}

Наконец, проверьте, завершился ли цикл без «выигрыша»:

if(turnNo == 9)
    message_its_a_draw(); // no-one wins

Функция для проверки выигрышной позиции может выглядеть так:

int test_for_win_position(int x, int y)
{
    char mark = matrix[x][y];

    // check a column
    if(matrix[x][0] == mark && matrix[x][1] == mark && matrix[x][2] == mark)
        return 1;

    // check a row
    if(matrix[0][y] == mark && matrix[1][y] == mark && matrix[2][y] == mark)
        return 1;

    // check one diagonal
    if(x==y)
        if(matrix[0][0] == mark && matrix[1][1] == mark && matrix[2][2] == mark)
            return 1;

    // check another diagonal
    if(x+y==2)
        if(matrix[0][2] == mark && matrix[1][1] == mark && matrix[2][0] == mark)
            return 1;

    // current player has not won (yet)
    return 0;
}
person CiaPan    schedule 17.11.2015