我用JavaScript编写了一个带有面向对象范例的2048年游戏。游戏板上有一个二维数组,每个平铺都有一个整数.
以下是实现:
class Game {
SIZE = 4
constructor() {
this.board = Array.from({ length: this.SIZE * this.SIZE }, () => 0).reduce(
(arrays, curr) => {
const lastArray = arrays[arrays.length - 1]
if (lastArray.length < this.SIZE) lastArray.push(curr)
else arrays.push([curr])
return arrays
},
[[]]
)
this.isWin = false
this._init()
}
_init() {
const pickedTiles = this._randomlyPick(2)
for (const [row, col] of pickedTiles) {
this.board[row][col] = Game.generateTile()
}
}
static generateTile() {
if (Math.random() > 0.5) return 2
return 4
}
_getEmptyTiles() {
const emptyTiles = []
for (let row = 0; row < this.SIZE; row++) {
for (let col = 0; col < this.SIZE; col++) {
if (this.board[row][col] === 0) emptyTiles.push([col, row])
}
}
return emptyTiles
}
_randomlyPick(numOfItems) {
const emptyTiles = this._getEmptyTiles()
for (let i = 0; i < numOfItems; i++) {
const toSwap = i + Math.floor(Math.random() * (emptyTiles.length - i))
;[emptyTiles[i], emptyTiles[toSwap]] = [emptyTiles[toSwap], emptyTiles[i]]
}
return emptyTiles.slice(0, numOfItems)
}
spawn() {
// randomly spawn empty tiles with 2 or 4
const [emtpyTile] = this._randomlyPick(1)
this.board[emtpyTile[0]][emtpyTile[1]] = Game.generateTile()
}
play(dir) {
if (this.canPlay()) {
switch (dir) {
case Game.moveUp:
this._mergeUp()
break
case Game.moveRight:
this._mergeRight()
break
case Game.moveLeft:
this._mergeLeft()
break
case Game.moveDown:
this._mergeDown()
break
}
this.spawn()
return true
}
return false
}
checkIsWin() {
return this.isWin
}
static peek(array) {
return array[array.length - 1]
}
static zip(arrays) {
const result = []
for (let i = 0; i < arrays[0].length; ++i) {
result.push(arrays.map((array) => array[i]))
}
return result
}
_mergeRowRight(sparseRow) {
const row = sparseRow.filter((x) => x !== 0)
const result = []
while (row.length) {
let value = row.pop()
if (Game.peek(row) === value) value += row.pop()
result.unshift(value)
}
while (result.length < 4) result.unshift(0)
return result
}
_mergeRowLeft(row) {
return this._mergeRowRight([...row].reverse()).reverse()
}
_mergeUp() {
this.board = Game.zip(Game.zip(this.board).map(row => this._mergeRowLeft(row)))
}
_mergeDown() {
this.board = Game.zip(Game.zip(this.board).map(row => this._mergeRight(row)))
}
_mergeRight() {
this.board = this.board.map((row) => this._mergeRowRight(row))
}
_mergeLeft() {
this.board = this.board.map((row) => this._mergeRowLeft(row))
}
canPlay() {
const dirs = [
[0, 1],
[1, 0],
[-1, 0],
[0, -1],
]
const visited = new Set()
for (let row = 0; row < this.SIZE; row++) {
for (let col = 0; col < this.SIZE; col++) {
if (visited.has([row, col].toString())) continue
const tile = this.board[row][col]
if (tile === 2048) {
this.isWin = true
return false
}
if (tile === 0) return true
for (const [dx, dy] of dirs) {
if (this.board[row + dx]?.[col + dy] === tile) return true
}
visited.add([row, col].toString())
}
}
return false
}
}
Game.moveUp = Symbol('moveUp')
Game.moveDown = Symbol('moveUp')
Game.moveLeft = Symbol('moveUp')
Game.moveRight = Symbol('moveUp')
const game = new Game()
console.log(game.board);
game.play(Game.moveUp)
console.log(game.board);
game.play(Game.moveRight)
console.log(game.board);欢迎任何反馈意见。具体来说,我想知道:
zip这样的使用函数来沿对角线翻转板是否是惯用的?symbol表示用户可以进行的移动的方向,并将它们作为实例变量附加到类中。同样,我也不确定这在OO中是否是惯用的,因为我对这一范式有点陌生。发布于 2021-03-09 14:57:32
关于符号的使用的快速回答。
“最后,我使用符号来表示用户可以进行的移动的方向,并将它们作为实例变量附加到类中。同样,我不确定这在OO中是否是惯用的,因为我对这个范例有点陌生。”
创建符号有两个原因
Symbol("foo") === Symbol("foo");总是错误的。Symbol.for("foo") === Symbol.for("foo");在全球范围内是正确的。在使用模块时很有用。见上文
Symbol()比创建唯一数字i++慢2个数量级。JSON.stringify({a: symbol("foo")})创建"{}"我提到这些缺点,虽然它们在这种情况下不适用,但相反,使用它们可能成为习惯,如果不知道问题,就很容易被发现。
你对符号的使用习惯吗?
不是的。对唯一性的要求并不能保证增加复杂性。使用一组数字就可以很容易地实现识别移动的需要。
Game.moveUp = 0;
Game.moveDown = 1;
Game.moveLeft = 2;
Game.moveRight = 3;不存在冲突的可能性,因为唯一性域仅限于这4个实体。
编码的第一条规则。在一切条件相同的情况下,最简单的解决办法是最好的。
名字写得不好。
如果您找到一组变量名,它们都以相同的名称/前缀开头,这是一个符号,表示它应该是一个对象。
使用块作用域计数器的示例枚举。
{
let i = 0;
Game.moves = {
up: i++,
down: i++,
left: i++,
right: i++,
};
}或者更好的常量
{
let i = 0;
Game.moves = Object.freeze({
UP: i++,
DOWN: i++,
LEFT: i++,
RIGHT: i++,
});
}https://codereview.stackexchange.com/questions/256691
复制相似问题