lazzzycat (lazzzycat) wrote,
lazzzycat
lazzzycat

Categories:

Пишем простой сапер для Андроида. Часть 3

И наконец самое главное – логика игры.

Все в логике вертится вокруг ячейки — класса Cell.
Экземпляр класса Cell имеет два параметра CellState — открыта, закрыта или помечена ячейка
MineState — есть/нет мины, количество мин в соседях.

При создании ячейки — ставим состояние — мин нет, ячейка закрыта
Есть функции установки и получения состояния ячейки.

Объявляем массив ячеек, соответствующий игровому полю
Cell [] [] CellField;

А теперь собственно класс логики игры MinesLohic

В конструкторе устанавливается переменные и вызывается функция первичного засева поля

public MinesLohic(int rows, int cols, int MinesNumber)
{
Cols = cols;
Rows = rows;
Mines=MinesNumber;
CellField = new Cell[Rows][Cols];
//Установка мин
initField(MinesNumber);
}


Установка на игровом поле мин случайным образом
Создаем экземпляры ячеек поля
Случайным образом генерируем мины

В цикле для каждой не занятой миной ячейки подсчитываем, сколько мин в «Соседях»

private void initField(int minesNumber)
{
for (int i = 0; i < Rows; ++i)
for (int j = 0; j < Cols; ++j)
CellField[i][j]= new Cell();

Random rnd = new Random(System.currentTimeMillis());
for (int i = 0; i < minesNumber; ++i)
{
int cc;
int cr;
do
{
cc = rnd.nextInt(Cols);
cr = rnd.nextInt(Rows);
}
while (isNotMineSet(cr, cc)!=true);
CellField[cr][cc].SetMineState(MINE_YES);

}

// расстановка количества мин в округе ячейки
for (int i = 0; i < Rows; i++)
for (int j = 0; j < Cols; j++)
{
if (CellField[i][j].MineState !=MINE_YES) // мины нет в этой ячейке
{
for (int k=-1;k<2; k++)
{
if (isMineSet(i+k, j-1)==true ) // есть мина в этой клетке
CellField[i][j].MineState ++;
if ((isMineSet(i+k, j)==true )&&(i+k!=i)) // есть мина в этой клетке
CellField[i][j].MineState ++;
if (isMineSet(i+k, j+1)==true ) // есть мина в этой клетке
CellField[i][j].MineState ++;
}
}
}
}


Функция reNew начала игры по нажатию на кнопку — очистка поля и генерация мин
все как в предыдущей функции, только вместо создания экземпляров ячеек — обнуление уже существующих

Три проверочных функции, используемые для проверки состояния ячейки isNotMineSet,isMineSet,isCellCheck.
Главное — функции проверяют выход за границы поля и возвращают False в случае выхода.
Такое поведение облегчает алгоритм открытия ячейки или обработки поля.

public Boolean isNotMineSet(int row, int col)
{ if (row < 0 || row >= Rows || col < 0 || col >= Cols)
return false;
if (CellField[row][col].GetMineState()==MINE_NO)
return true;
else
return false;
}


Осталось две вещи — обработка долгого нажатия — установка/удаление пометки
и обработка обычного нажатия.

Обработка долгого нажатия на ячейку. Все просто — флажок есть — снимаем, нет флажка — устанавливаем.

public void LongTapCell(int position)
{
int r = position / Cols;
int c = position % Cols;
switch (CellField[r][c].CellState){
case CELL_CLOSE: //ячейка закрыта
CellField[r][c].CellState=CELL_CHECK;
break;
case CELL_OPEN: //ячейка открыта

break;
case CELL_CHECK: //Установлен флажок
CellField[r][c].CellState= CELL_CLOSE;
break;
}

}


Обработка обычного нажатия. Может быть три варианта
Есть мина — вызываем функцию Boom
В противном случае — открываем ячейку.
Если ячейка пуста — вызываем ClearAround;

public void TapCell(int position)
{
int r = position / Cols;
int c = position % Cols;
switch (CellField[r][c].CellState){
case CELL_CLOSE: //ячейка закрыта
CellField[r][c].CellState=CELL_OPEN;
switch (CellField[r][c].MineState){
case MINE_NO: //мин в округе нет
ClearAround(position);
break;
case MINE_YES: //мин в округе нет
//Обрабатываем процесс подрыва мины
Boom(position);
break;
}
case CELL_OPEN: //ячейка открыта

break;
case 2: //ячейка помечена

break;
}

}


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

// если ячейка пустая – открывает вокруг ячейки без мин
public void ClearAround(int position)
{

OpenCell(position / Cols, position % Cols);

for (int i = position / Cols; i < Rows; i++)
for (int j = 0; j < Cols; j++)
{
if ((CellField[i][j].MineState ==MINE_NO)&&(CellField[i][j].CellState==CELL_OPEN)) // мины нет в этой ячейке и она не помечена
{
for (int k=-1;k<2; k++)
{
if ((isMineSet(i+k, j-1)==false )&&(isCellCheck(i+k,j-1)!=true))
// есть мина в этой клетке
OpenCell(i+k,j-1);
if ((isMineSet(i+k, j)==false )&&(isCellCheck(i+k,j)!=true)) // есть мина в этой клетке
OpenCell(i+k,j);
if ((isMineSet(i+k, j+1)==false )&&(isCellCheck(i+k,j+1)!=true)) // есть мина в этой клетке
OpenCell(i+k, j+1);
}
}
}

for (int i = Rows-1; i >= 0; i--)
for (int j = Cols-1;j>=0; j--)
{
if ((CellField[i][j].MineState ==MINE_NO)&&(CellField[i][j].CellState==CELL_OPEN)) // мины нет в этой ячейке и она не помечена
{
for (int k=-1;k<2; k++)
{
if ((isMineSet(i+k, j-1)==false )&&(isCellCheck(i+k,j-1)!=true))
// есть мина в этой клетке
OpenCell(i+k,j-1);
if ((isMineSet(i+k, j)==false )&&(isCellCheck(i+k,j)!=true)) // есть мина в этой клетке
OpenCell(i+k,j);
if ((isMineSet(i+k, j+1)==false )&&(isCellCheck(i+k,j+1)!=true)) // есть мина в этой клетке
OpenCell(i+k, j+1);
}
}
}

}


Подрыв мины и очистка поля. Вызываем плеер для проигрования файла со звуком взрыва и открываем все поле. Там, где неправильно помечена ячейка — ставим перечеркнутый флажок

public void Boom(int position)
{
//Воспроизводим звук взрыва
MediaPlayer mp = MediaPlayer.create(FieldActivity.activity, R.raw.boom);
mp.start();
//Открываем ячейки
for (int i = 0; i < Rows; i++)
for (int j = 0; j < Cols; j++)
{
switch(CellField[i][j].CellState){
case CELL_CLOSE:
CellField[i][j].CellState=CELL_OPEN;
break;
case CELL_CHECK:
// Если неверно стоит флажок – заменяем на перечеркнутый флажок
if (CellField[i][j].MineState!=MINE_YES)
CellField[i][j].CellState=3;
break;
}
}
}


Вот и все. Самостоятельно сделайте таймер, счетчик найденых мин и таблицу лучших результатов.
Tags: android, программирование
Subscribe
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 0 comments