2048

游戏

适配设备:Pro

作者:shandongximo

下载量:34 · 点赞:0

可信源码风险提示:保存或下载前请阅读源码和权限声明。
SHA-2560340f99937b94289f36887149deeed920bd9fcab864037d37d2754b0ee47ab68

const SIZE = 4;
// Classic 2048 tiles, with a dark empty cell for the watch's black background.
const TILE_BACKGROUNDS = {
  0: '#3d3630',
  2: '#eee4da',
  4: '#ede0c8',
  8: '#f2b179',
  16: '#f59563',
  32: '#f67c5f',
  64: '#f65e3b',
  128: '#edcf72',
  256: '#edcc61',
  512: '#edc850',
  1024: '#edc53f',
  2048: '#edc22e'
};
const BUTTON_BACKGROUND = '#8f7a66';
const LIGHT_TEXT = '#f9f6f2';

function emptyBoard() {
  return [
    0, 0, 0, 0,
    0, 0, 0, 0,
    0, 0, 0, 0,
    0, 0, 0, 0
  ];
}

function addTile(board) {
  const empty = [];
  for (let i = 0; i < board.length; i += 1) {
    if (board[i] === 0) empty.push(i);
  }
  if (empty.length === 0) return board;
  const next = board.slice();
  const target = empty[Math.floor(Math.random() * empty.length)];
  next[target] = Math.random() < 0.9 ? 2 : 4;
  return next;
}

function newGame() {
  return {
    board: addTile(addTile(emptyBoard())),
    score: 0,
    message: ''
  };
}

function lineIndices(direction, line) {
  const indices = [];
  for (let offset = 0; offset < SIZE; offset += 1) {
    if (direction === 'left') indices.push(line * SIZE + offset);
    if (direction === 'right') indices.push(line * SIZE + (SIZE - 1 - offset));
    if (direction === 'up') indices.push(offset * SIZE + line);
    if (direction === 'down') indices.push((SIZE - 1 - offset) * SIZE + line);
  }
  return indices;
}

function mergeLine(values) {
  const packed = values.filter(value => value !== 0);
  const merged = [];
  let gained = 0;
  for (let i = 0; i < packed.length; i += 1) {
    if (i + 1 < packed.length && packed[i] === packed[i + 1]) {
      const value = packed[i] * 2;
      merged.push(value);
      gained += value;
      i += 1;
    } else {
      merged.push(packed[i]);
    }
  }
  while (merged.length < SIZE) merged.push(0);
  return { values: merged, gained };
}

function canMove(board) {
  for (let i = 0; i < board.length; i += 1) {
    if (board[i] === 0) return true;
    const row = Math.floor(i / SIZE);
    const column = i % SIZE;
    if (column + 1 < SIZE && board[i] === board[i + 1]) return true;
    if (row + 1 < SIZE && board[i] === board[i + SIZE]) return true;
  }
  return false;
}

function tileColor(value) {
  if (value <= 4) return '#776e65';
  // Dark digits stay readable on the bright gold tiles.
  if (value >= 128 && value <= 2048) return '#51473d';
  return LIGHT_TEXT;
}

const game = ui.signal(newGame());
console.log('2048 已开始;系统返回查看日志,轻点日志恢复游戏');

function move(direction) {
  const current = game.get();
  const board = current.board.slice();
  let gained = 0;
  let changed = false;

  for (let line = 0; line < SIZE; line += 1) {
    const indices = lineIndices(direction, line);
    const values = indices.map(index => board[index]);
    const result = mergeLine(values);
    gained += result.gained;
    for (let i = 0; i < SIZE; i += 1) {
      if (board[indices[i]] !== result.values[i]) changed = true;
      board[indices[i]] = result.values[i];
    }
  }

  if (!changed) {
    game.set({
      board: current.board,
      score: current.score,
      message: canMove(current.board) ? '这个方向不能移动' : '游戏结束'
    });
    return;
  }

  const nextBoard = addTile(board);
  console.log('2048', direction, '分数', current.score + gained);
  game.set({
    board: nextBoard,
    score: current.score + gained,
    message: canMove(nextBoard) ? '' : '游戏结束'
  });
}

function reset() {
  console.log('2048 新局');
  game.set(newGame());
}

// Coordinates are relative to this 324 x 156 control area.
// The four arrows form a cross; utility actions occupy the bottom corners.
const controls = ui.stack([
  ui.button('↑', () => move('up'), {
    id: 'up', x: 122, y: 0, width: 80, height: 50, lines: 1,
    background: BUTTON_BACKGROUND, color: LIGHT_TEXT
  }),
  ui.button('←', () => move('left'), {
    id: 'left', x: 36, y: 53, width: 80, height: 50, lines: 1,
    background: BUTTON_BACKGROUND, color: LIGHT_TEXT
  }),
  ui.button('→', () => move('right'), {
    id: 'right', x: 208, y: 53, width: 80, height: 50, lines: 1,
    background: BUTTON_BACKGROUND, color: LIGHT_TEXT
  }),
  ui.button('↓', () => move('down'), {
    id: 'down', x: 122, y: 106, width: 80, height: 50, lines: 1,
    background: BUTTON_BACKGROUND, color: LIGHT_TEXT
  }),
  ui.button('新局', reset, {
    id: 'reset', x: 0, y: 106, width: 100, height: 50, lines: 1,
    background: '#a47745', color: LIGHT_TEXT
  }),
  ui.button('退出', () => script.exit(), {
    id: 'exit', x: 224, y: 106, width: 100, height: 50, lines: 1,
    background: '#4d443b', color: LIGHT_TEXT
  })
], { id: 'controls', height: 156 });

ui.showHeader(false);
ui.render(() => {
  const state = game.get();
  const cells = state.board.map((value, index) => ({
    id: 'tile-' + index,
    text: value === 0 ? '' : String(value),
    background: TILE_BACKGROUNDS[value] || '#3c3a32',
    color: tileColor(value),
    size: value >= 1024 ? 18 : value >= 128 ? 21 : 25
  }));

  return ui.column([
    ui.text('2048 分数 ' + state.score, {
      id: 'score',
      size: 27,
      lines: 1,
      color: LIGHT_TEXT
    }),
    ui.grid(cells, {
      id: 'board',
      columns: 4,
      cellHeight: 50
    }),
    ui.text(state.message || '合并相同数字,得到 2048', {
      id: 'message',
      size: 18,
      lineHeight: 22,
      lines: 1,
      color: state.message === '游戏结束' ? '#f67c5f' : '#b9ada1',
      align: 'center'
    }),
    controls
  ], { id: 'game', gap: 4 });
});