Освой Processing играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Игра "Сапёр"

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

Напишем «Сапёр», в котором нужно просто нажимать на квадраты, надеясь, что под ними нет мины.

Первый шаг — определение состояния игры.


int gridW; // ширина сетки
int gridH; // высота сетки
int numMines; // число мин на поле
 
int[][] mines; // двумерный массив, 1 если есть мина, 0 если мины нет
boolean[][] flags; // entry is true if you have flagged that spot
boolean[][] revealed; // entry is true if that spot is revealed

Переменная int[][] mines позволяет легко подсчитать, сколько мин находится рядом с определённой позицией.


int calcNear(int x, int y) {
  int i = 0;
  for (int offsetX = -1; offsetX <= 1; offsetX++) {
    for (int offsetY = -1; offsetY <= 1; offsetY++) {
      i+=mines[offsetX+x][offsetY+y];
    }
  }
  return i;
}

После того, как мы уберём исключения, мы получим что-то похожее на это:


boolean outBounds(int x, int y){
  return x <0 || y <0 || x>= gridW || y >= gridH;
}

int calcNear(int x, int y) {
  if(outBounds(x,y))return 0;
  int i = 0;
  for (int offsetX = -1; offsetX <= 1; offsetX++) {
    for (int offsetY = -1; offsetY <= 1; offsetY++) {
      if (outBounds(offsetX+x, offsetY + y))continue;
      i+=mines[offsetX+x][offsetY+y];
    }
  }
  return i;
}

Главная задача самой игры — раскрывать квадраты, начиная с точки х, у.


void reveal(int x, int y){
  if(outBounds(x,y))return;
  if(revealed[x][y])return;
  revealed[x][y]=true;
  if(calcNear(x,y)!=0)return;
  reveal(x-1,y-1);
  reveal(x-1,y+1);
  reveal(x+1,y-1);
  reveal(x+1,y+1);
  reveal(x-1,y);
  reveal(x+1,y);
  reveal(x,y-1);
  reveal(x,y+1);
}

В итоге мы имеем следующий алгоритм:

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

Визуализация

У нас есть переменная cellSize, определяющая количество пикселей в каждом квадрате.


void settings(){
  size(gridW * cellSize, gridH * cellSize);
}

Таким образом мы создаём поле со сторонами gridW x gridH, где размеры каждого квадрата будут равняться значению cellSize.

Затем мы возвращаем переменные в изначальное состояние.


void setup(){
  //initialize and clear the arrays
  mines = new int[gridW][gridH];
  flags = new boolean[gridW][gridH];
  revealed = new boolean[gridW][gridH];
  for(int x=0;x < gridW;x++){
    for(int y = 0; y < gridH;y++){
      mines[x][y]=0;
      flags[x][y]=false;
      revealed[x][y]=false;
    }
  }
}

Для инициализации поля:


//Place numMines mines on the grid
void placeMines(){
  int i=0;
  while(i < numMines){//We don't want mines to overlap, so while loop
    int x=int(random(gridW));
    int y=int(random(gridH));
    if(mines[x][y]==1)continue;
    mines[x][y]=1;
    i++;
  }
}
//Clear the mines
void clearMines() {
  for (int x=0; x < gridW; x++) {
    for (int y=0; y < gridH; y++) {
      mines[x][y]=0;
    }
  }
}

Включаем обработку щелчков мыши.


//We don't want the first click to be a mine
boolean firstClick=true;
 
void mousePressed() {
  int x=int(mouseX/cellSize);
  int y=int(mouseY/cellSize);
  //Right-click is flagging or de-flagging a square
  if (mouseButton==RIGHT) {
    flags[x][y]=!flags[x][y];
    return;
  } else {//left-click
    //Avoid making the first click a mine
    if (firstClick) {
      firstClick=false;
      do {
        clearMines();
        placeMines();
      } while (calcNear(x,y)!=0);
    }
    //Check for game loss
    if (mines[x][y]!=0) {
      println("Dang!");
      exit();
    } else {//If game not lost, reveal starting from that square
      reveal(x, y);
    }
  }
}

Отрисовка игры.


void draw() {
  background(0);
  //For each cell
  for (int x=0; x0){
      //fill(0,255,0);
      //stroke(0,255,0);
      //}
 
      rect(x*cellSize, y*cellSize, cellSize, cellSize);
      //If there is a mine near this spot and it is revealed
      if (near>0&&revealed[x][y]) {
        fill(txtColor);
        noStroke();
        textAlign(LEFT, TOP);
        textSize(cellSize);
        text(""+near, x*cellSize, y*cellSize);
      }
    }
  }
}

Оригинал статьи.

На главную страницу Processing

Реклама