Деннис Ван

Вы когда-нибудь играли в Bejeweled? Bejeweled — игра-головоломка №1 в мире. Когда я учился в старшей школе, я много играл в Candy Crush Saga. Теперь я разработчик программного обеспечения с различными технологиями в разработке веб-приложений и мобильных приложений. Сегодня я хотел бы поделиться своим простым коротким кодом для поиска и удаления совпадений из случайно сгенерированной сетки конфет.

Основная зависимость, используемая в этом проекте, — lodash.

Во-первых, мы собираемся определить постоянные значения, обозначающие драгоценности.

const COLOR = {
  RED: '#E23822',
  BLUE: '#3192F1',
  PURPLE: '#5630D8',
  YELLOW: '#FFD449',
  GREEN: '#144B14',
  SILK: '#E3E3E3',
  GOLD: '#F9A620'
}
const JEWELS = [
  COLOR.RED,
  COLOR.BLUE,
  COLOR.PURPLE,
  COLOR.YELLOW,
  COLOR.GREEN,
  COLOR.YELLOW,
  COLOR.SILK,
  COLOR.GOLD
]

Затем мы импортируем некоторые вспомогательные функции lodash.

import { difference, indexOf, isEmpty, random, size, union } from 'lodash'

Затем мы создаем функцию generateGrid, которая создает сетку драгоценных камней из списка прототипов драгоценных камней и требуемого размера сетки.

const generateGrid = (prototypes, col, row) => {
  if (isEmpty(prototypes)) return []
  col = parseInt(col)
  row = parseInt(row)
  if (col <= 0 || row <= 0) throw Error('Invalid props')
  var grid = []
  for (var i = 0; i < row; i++) {
    var eachRow = []
    for (var j = 0; j < col; j++) {
    eachRow.push(prototypes[random(size(prototypes) - 1)])
  }
  grid.push(eachRow)
}

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

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

const findMatches = grid => {
  var matches = []
  var groups = []
  for (var i = 0; i < grid.length; i++) {
    var tempArr = grid[i]
    groups = []
    for (var j = 0; j < tempArr.length; j++) {
      if (j < tempArr.length - 2) {
        if (grid[i][j] && grid[i][j + 1] && grid[i][j + 2]) {
          if (grid[i][j] === grid[i][j + 1] && grid[i][j + 1] === grid[i][j + 2]) {
            if (!isEmpty(groups) && indexOf(groups, grid[i][j]) === -1) {
              matches.push(groups)
              groups = []
            }
            indexOf(groups, grid[i][j]) === -1 && groups.push({ row: i, col: j })

            indexOf(groups, grid[i][j + 1]) === -1 && groups.push({ row: i, col: j + 1 })

            indexOf(groups, grid[i][j + 2]) === -1 && groups.push({ row: i, col: j + 2 })
          }
        }
      }
    }
    !isEmpty(groups) && matches.push(groups)
  }
  for (j = 0; j < grid.length; j++) {
    tempArr = grid[j]
    groups = []
    for (i = 0; i < tempArr.length; i++) {
      if (i < tempArr.length - 2) {
        if (grid[i][j] && grid[i + 1][j] && grid[i + 2][j]) {
          if (grid[i][j] === grid[i + 1][j] && grid[i + 1][j] === grid[i + 2][j]) {
            if (!isEmpty(groups) && indexOf(groups, grid[i][j]) === -1) {
              matches.push(groups)
              groups = []
            }
            indexOf(groups, grid[i][j]) === -1 && groups.push({ row: i, col: j })
            indexOf(groups, grid[i + 1][j]) === -1 && groups.push({ row: i + 1, col: j })
            indexOf(groups, grid[i + 2][j]) === -1 && groups.push({ row: i + 2, col: j })
          }
        }
      }
    }
    !isEmpty(groups) && matches.push(groups)
  }
  var result = []
  for (i = 0; i < matches.length; i++) {
    const row = matches[i]
    result = union(result, row)
  }

  return result
}

Теперь мы можем удалить все совпадения из сетки, вызвав вышеуказанную функцию. Что нам нужно сделать, так это не удалять. Их приходится заменять другими драгоценностями, чтобы избежать дополнительных совпадений. Для этого мы собираемся выбрать драгоценный камень на основе соседей вокруг каждой соответствующей ячейки. Следующий код работает за нас. Добавьте следующие строки в функцию generateGrid.

const matches = findMatches(grid)
if (isEmpty(matches)) return grid
for (i = 0; i < matches.length; i++) {
  var neighbors = []
  matches[i].col > 0 && neighbors.push({ row: matches[i].row, col: matches[i].col - 1 })
  matches[i].col < col - 1 && neighbors.push({ row: matches[i].row, col: matches[i].col + 1 })
  matches[i].row > 0 && neighbors.push({ row: matches[i].row - 1, col: matches[i].col })
  matches[i].row < row - 1 && neighbors.push({ row: matches[i].row + 1, col: matches[i].col })
  const candidateJewels = difference(prototypes, neighbors.map(neighbor => grid[neighbor.row][neighbor.col]))
  if (!isEmpty(candidateJewels)) {
    grid[matches[i].row][matches[i].col] =
    candidateJewels[random(size(candidateJewels) - 1)]
  }
}
return grid

Наконец, вы вызываете функцию generateGrid, чтобы получить допустимую сетку без совпадений.

console.log(generateGrid(JEWELS, 8, 8))

Если вас интересует его визуализация, посетите мой репозиторий Github.

Большое спасибо, что прочитали мой пост!