请注意以下棋类完全由本人原创构思+ai代码构成,简单梳理可玩性后推出【规则封装至程序】
瘸子棋.py
import numpy as np
import os
import sys
from enum import Enum
class GameState(Enum):
MENU = 1
PLAYING = 2
RULES = 3
GAME_OVER = 4
class LameChessGame:
def __init__(self, n=17):
if n % 2 == 0:
n += 1 # 确保棋盘有中心点
self.n = n
self.board = np.full((n, n), ' ', dtype=str) # 初始化棋盘
self.center = n // 2
self.pawn_pos = (self.center, self.center) # 瘸子初始位置
self.board[self.center][self.center] = 'P' # 放置瘸子
# 设置终点位置
self.goal_A = (self.center, 0)
self.goal_B = (self.center, n - 1)
self.board[self.goal_A] = 'G' # A终点标记
self.board[self.goal_B] = 'G' # B终点标记
self.current_player = 'A' # A先手
self.valves = {'A': [], 'B': []} # 存储转向阀位置
self.turn_positions = [] # 记录转动路径
self.move_count = 0
self.winner = None
self.game_state = GameState.MENU
self.valid_placements = [] # 存储当前可放置位置
self.preview_dict = {} # 预览位置字典(位置->数字编号)
self.consecutive_no_moves = 0 # 连续无移动回合计数
def clear_screen(self):
"""清屏函数"""
os.system('cls' if os.name == 'nt' else 'clear')
def print_header(self):
"""打印游戏标题"""
print("=" * 60)
print(f"{'瘸 子 棋 游 戏':^60}")
print("=" * 60)
print(f"{'棋盘大小: ' + str(self.n) + 'x' + str(self.n):^60}")
print("=" * 60)
def print_board(self, preview_dict=None):
"""打印当前棋盘状态"""
if preview_dict is None:
preview_dict = {}
# 列坐标
col_header = " " + " ".join(f"{i:02d}" for i in range(self.n))
print(col_header)
print(" ┌" + "──" * self.n + "─┐")
for r in range(self.n):
row_str = f"{r:02d} │ "
for c in range(self.n):
pos = (r, c)
cell = self.board[r][c]
# 显示预览位置数字
display_char = None
if pos in preview_dict:
num = preview_dict[pos]
display_char = f'\033[1;37;44m{num}\033[0m' # 蓝底白字加粗
if display_char:
row_str += display_char
elif cell == 'G' and pos == self.goal_A: # A终点
row_str += '\033[91m★\033[0m'
elif cell == 'G' and pos == self.goal_B: # B终点
row_str += '\033[94m★\033[0m'
elif cell == 'P': # 瘸子
row_str += '\033[92m♞\033[0m'
elif cell == 'A': # A阀门
row_str += '\033[91m■\033[0m'
elif cell == 'B': # B阀门
row_str += '\033[94m■\033[0m'
elif cell == ' ': # 空位
row_str += '·'
else: # 其他
row_str += f'{cell}'
row_str += ' ' # 格子间空格
row_str += "│"
print(row_str)
print(" └" + "──" * self.n + "─┘")
print(
f"玩家 \033[91mA\033[0m 目标: ({self.goal_A[0]}, {self.goal_A[1]}) | 玩家 \033[94mB\033[0m 目标: ({self.goal_B[0]}, {self.goal_B[1]})")
print(f"当前回合: 玩家 \033[{'91' if self.current_player == 'A' else '94'}m{self.current_player}\033[0m")
print(f"移动次数: {self.move_count}")
def get_adjacent_positions(self, pos):
"""获取相邻位置"""
r, c = pos
positions = []
for dr, dc in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
nr, nc = r + dr, c + dc
if 0 <= nr < self.n and 0 <= nc < self.n:
positions.append((nr, nc))
return positions
def place_valve(self):
"""放置转向阀阶段(强制放置)"""
self.clear_screen()
self.print_header()
adjacent = self.get_adjacent_positions(self.pawn_pos)
self.valid_placements = [pos for pos in adjacent if self.board[pos] == ' ' or self.board[pos] == 'G']
self.preview_dict = {} # 重置预览字典
# 如果没有可放置位置,直接跳过
if not self.valid_placements:
print("\n\033[33m无可放置位置,跳过放置阶段\033[0m")
input("按Enter继续...")
return False
# 创建位置-数字映射
for i, pos in enumerate(self.valid_placements):
if i < 9: # 只显示1-9
self.preview_dict[pos] = i + 1
# 打印棋盘并显示预览位置数字
self.print_board(preview_dict=self.preview_dict)
print(f"\n\033[92m请选择放置位置:\033[0m")
for i, pos in enumerate(self.valid_placements):
# 为每个选项显示数字编号
num_display = f"[\033[44m{i+1}\033[0m]" if i < 9 else f"[{i+1}]"
print(f"{num_display}: ({pos[0]}, {pos[1]})")
while True:
try:
choice = input("请输入放置位置编号: ")
choice = int(choice)
if 1 <= choice <= len(self.valid_placements):
pos = self.valid_placements[choice - 1]
r, c = pos
# 放置转向阀
self.board[r][c] = self.current_player
self.valves[self.current_player].append((r, c))
print(
f"玩家 \033[{'91' if self.current_player == 'A' else '94'}m{self.current_player}\033[0m 在 ({r},{c}) 放置了转向阀")
return True
else:
print("\033[33m编号无效,请重新选择\033[0m")
except:
print("\033[33m输入无效,请重新选择\033[0m")
def is_valve_in_straight_line(self, valve_pos):
"""检查阀门是否在瘸子的同一行或同一列(水平或垂直方向)"""
r1, c1 = self.pawn_pos
r2, c2 = valve_pos
return r1 == r2 or c1 == c2
def check_path_to_valve(self, valve_pos):
"""检查到阀门的路径是否畅通(只检查水平和垂直方向)"""
r1, c1 = self.pawn_pos
r2, c2 = valve_pos
# 首先检查是否在水平或垂直方向
if not self.is_valve_in_straight_line(valve_pos):
return False
# 确定方向向量
dr = r2 - r1
dc = c2 - c1
# 标准化方向
step_r = 0 if dr == 0 else 1 if dr > 0 else -1
step_c = 0 if dc == 0 else 1 if dc > 0 else -1
# 计算步数
steps = max(abs(dr), abs(dc))
# 检查路径上的每个格子(不包括起点和终点)
for i in range(1, steps):
r = r1 + i * step_r
c = c1 + i * step_c
cell = self.board[r][c]
# 只允许空格、终点或当前玩家的阀门
if cell == ' ' or cell == 'G' or cell == self.current_player:
continue
else:
return False
return True
def calculate_rotation(self, valve_pos):
"""计算旋转后的位置"""
pr, pc = self.pawn_pos
vr, vc = valve_pos
# 计算相对向量
dr, dc = pr - vr, pc - vc
# 计算旋转后的位置 (90度)
clockwise = (vr - dc, vc + dr) # 顺时针
counter_clockwise = (vr + dc, vc - dr) # 逆时针
valid_positions = []
for direction, pos in [('顺时针', clockwise), ('逆时针', counter_clockwise)]:
r, c = pos
# 检查位置是否在棋盘内
if not (0 <= r < self.n and 0 <= c < self.n):
continue
# 检查落点是否为空或终点(仅允许移动到自己的终点)
cell = self.board[r][c]
if cell == ' ':
pass # 空位总是允许
elif cell == 'G': # 终点位置
# 玩家A只能移动到A终点,玩家B只能移动到B终点
if (self.current_player == 'A' and (r, c) != self.goal_A) or \
(self.current_player == 'B' and (r, c) != self.goal_B):
continue # 跳过对方终点
else:
continue # 其他情况不允许
# 检查路径是否畅通
if not self.check_path_to_valve(valve_pos):
continue
# 避免回到原点
if len(self.turn_positions) > 1 and pos == self.turn_positions[-2]:
continue
valid_positions.append((direction, pos))
return valid_positions
def rotate_pawn(self):
"""转动瘸子阶段"""
self.turn_positions = [self.pawn_pos]
valve_usage = {} # 本回合阀门使用计数(每回合重置)
moved = False # 标记本回合是否进行了移动
while True:
self.clear_screen()
self.print_header()
available_rotations = []
self.preview_dict = {} # 重置预览字典
# 检查所有可用的转向阀(只考虑水平和垂直方向)
for valve_pos in self.valves[self.current_player]:
# 首先检查阀门是否在水平和垂直方向
if not self.is_valve_in_straight_line(valve_pos):
continue
# 检查使用次数(本回合内)
if valve_usage.get(valve_pos, 0) < 3:
rotations = self.calculate_rotation(valve_pos)
for direction, pos in rotations:
available_rotations.append((valve_pos, direction, pos))
# 创建预览位置字典
for idx, (_, _, pos) in enumerate(available_rotations, start=1):
if idx <= 9: # 只显示1-9
self.preview_dict[pos] = idx
# 打印棋盘并显示预览位置数字
self.print_board(preview_dict=self.preview_dict)
if not available_rotations:
if not moved: # 如果整个回合都没移动过
print("\n\033[33m无可用的转动操作\033[0m")
else:
print("\n\033[33m无法继续转动\033[0m")
input("按Enter结束转动阶段...")
return moved
# 显示可用操作
print("\n\033[92m可用转动操作:\033[0m")
for i, (valve, direction, pos) in enumerate(available_rotations):
# 为每个选项使用不同颜色
direction_color = '\033[96m' if direction == '顺时针' else '\033[95m'
# 如果位置有预览数字,显示在操作中
preview_num = self.preview_dict.get(pos, ' ')
preview_display = f"[\033[44m{preview_num}\033[0m]" if pos in self.preview_dict else "[ ]"
print(
f"{preview_display} {i + 1}: 使用阀({valve[0]},{valve[1]}) {direction_color}{direction}\033[0m → 移动到({pos[0]},{pos[1]})")
print("\033[48;5;196m 0 \033[0m: 结束转动阶段")
# 玩家选择
try:
choice = input("请选择操作编号: ")
if choice == '0':
break
choice = int(choice)
if 1 <= choice <= len(available_rotations):
valve_pos, direction, new_pos = available_rotations[choice - 1]
# 更新使用次数
valve_usage[valve_pos] = valve_usage.get(valve_pos, 0) + 1
# 移动瘸子
old_r, old_c = self.pawn_pos
new_r, new_c = new_pos
self.board[old_r][old_c] = ' '
self.board[new_r][new_c] = 'P'
self.pawn_pos = (new_r, new_c)
self.turn_positions.append(new_pos)
moved = True # 标记已移动
self.move_count += 1
direction_color = '\033[96m' if direction == '顺时针' else '\033[95m'
print(
f"\n玩家 \033[{'91' if self.current_player == 'A' else '94'}m{self.current_player}\033[0m 使用阀{valve_pos} {direction_color}{direction}\033[0m 移动到 ({new_r},{new_c})")
# 检查胜利条件
if (self.current_player == 'A' and new_pos == self.goal_A) or \
(self.current_player == 'B' and new_pos == self.goal_B):
self.winner = self.current_player
return True
else:
print("\033[33m编号无效,请重新选择\033[0m")
except:
print("\033[33m输入无效,请重新选择\033[0m")
return moved
def play_turn(self):
"""执行一个完整回合"""
# 放置阶段(强制放置)
placed = self.place_valve()
# 转动阶段
moved = self.rotate_pawn()
# 如果本回合有移动操作,重置无移动计数
if placed or moved:
self.consecutive_no_moves = 0
else:
self.consecutive_no_moves += 1
print(f"\n\033[33m玩家 {self.current_player} 本回合无有效操作\033[0m")
print(f"连续无操作回合: {self.consecutive_no_moves}/2")
input("按Enter继续...")
# 检查胜利条件
if self.winner:
return True
# 切换玩家
self.current_player = 'B' if self.current_player == 'A' else 'A'
return False
def show_rules(self):
"""显示游戏规则(更新版)"""
self.clear_screen()
self.print_header()
print("\n\033[93m游戏规则:\033[0m")
print("1. 在一个n×n的棋盘上,A和B轮流博弈")
print("2. 棋盘中心有一个♞棋子(瘸子)")
print("3. 每回合玩家必须:")
print(" a. 在瘸子相邻的4个方向放置一个转向阀(■)(如有位置)")
print(" b. 使用自己的转向阀进行90度旋转移动(如有可能)")
print("4. 关键限制:")
print(" - 只能使用与瘸子在同一行或同一列(水平或垂直方向)的阀门")
print(" - 不能使用对角线方向上的阀门")
print("5. 旋转规则:")
print(" - 路径上不能有对方阀门")
print(" - 落点必须是空位(·)或己方终点(★)")
print(" - 同一阀门一回合最多使用3次")
print("6. 阀门使用:")
print(" - 阀门永久有效,可跨回合使用")
print("7. 胜利条件:")
print(" - 玩家A将♞移动到左侧终点★(中心行,第0列)")
print(" - 玩家B将♞移动到右侧终点★(中心行,最后一列)")
print("8. 平局条件:")
print(" - 双方连续两回合无法进行任何移动")
print("\n\033[93m操作说明:\033[0m")
print("- 放置阶段:棋盘上蓝色数字显示的位置是可选放置位置")
print("- 放置阶段:选择对应数字编号放置阀门")
print("- 转动阶段:棋盘上蓝色数字显示的位置是可选目标位置")
print("- 转动阶段:选择对应数字编号进行转动")
print("- 转动选项显示方向:\033[96m顺时针\033[0m 或 \033[95m逆时针\033[0m")
print("\n按Enter返回主菜单...")
input()
def show_menu(self):
"""显示主菜单"""
self.clear_screen()
self.print_header()
print("\n\033[93m主菜单:\033[0m")
print("\033[48;5;40m 1 \033[0m 开始新游戏")
print("\033[48;5;45m 2 \033[0m 游戏规则")
print("\033[48;5;196m 3 \033[0m 退出游戏")
while True:
choice = input("\n请选择: ")
if choice == '1':
self.__init__(self.n) # 重置游戏
self.game_state = GameState.PLAYING
return
elif choice == '2':
self.game_state = GameState.RULES
return
elif choice == '3':
sys.exit(0)
else:
print("\033[33m无效选择,请重新输入\033[0m")
def show_game_over(self):
"""显示游戏结束画面"""
self.clear_screen()
self.print_header()
self.print_board()
if self.winner == 'DRAW':
print("\n╔════════════════════════╗")
print("║ \033[93m平 局!\033[0m ║")
print("╚════════════════════════╝")
print("双方连续两回合无法进行任何移动")
else:
color = '\033[91m' if self.winner == 'A' else '\033[94m'
print(f"\n╔════════════════════════╗")
print(f"║ 玩家 {color}{self.winner}\033[0m \033[93m获胜!\033[0m ║")
print("╚════════════════════════╝")
print("\n\033[93m选择:\033[0m")
print("\033[48;5;40m 1 \033[0m 返回主菜单")
print("\033[48;5;196m 2 \033[0m 退出游戏")
while True:
choice = input("\n请选择: ")
if choice == '1':
self.game_state = GameState.MENU
return
elif choice == '2':
sys.exit(0)
else:
print("\033[33m无效选择,请重新输入\033[0m")
def play_game(self):
"""主游戏循环"""
self.consecutive_no_moves = 0
while True:
if self.game_state == GameState.MENU:
self.show_menu()
elif self.game_state == GameState.RULES:
self.show_rules()
self.game_state = GameState.MENU
elif self.game_state == GameState.PLAYING:
game_ended = self.play_turn()
if self.winner:
self.game_state = GameState.GAME_OVER
continue
# 检查平局条件
if self.consecutive_no_moves >= 2:
self.winner = 'DRAW'
self.game_state = GameState.GAME_OVER
elif self.game_state == GameState.GAME_OVER:
self.show_game_over()
# 启动游戏
if __name__ == "__main__":
# 检查终端尺寸
if os.name == 'nt': # Windows
os.system('mode con: cols=80 lines=50')
game = LameChessGame(17) # 17x17棋盘
game.play_game()
推推棋.py
import numpy as np
import random
import time
import os
class PushGame:
def __init__(self, rows=7, cols=5): # 修改为7行5列
self.set_board_size(rows, cols)
self.reset_game()
def set_board_size(self, rows, cols):
"""设置棋盘大小并计算相关参数"""
self.rows = rows
self.cols = cols
self.center = (rows // 2, cols // 2) # 7//2=3, 5//2=2 -> (3,2)
self.win_count = 1 # 获胜所需中心点计数
def reset_game(self):
"""重置游戏状态"""
# 创建空棋盘
self.board = np.full((self.rows, self.cols), '.', dtype='U1')
# 初始化中心点计数
self.center_counts = [0, 0] # [A的中心点计数, B的中心点计数]
# 初始化玩家位置 - 底边和顶边全放棋子
# A (玩家0) 在底部一行(所有列)
for j in range(self.cols):
self.board[-1, j] = 'A'
# B (玩家1) 在顶部一行(所有列)
for j in range(self.cols):
self.board[0, j] = 'B'
self.current_player = 0 # 0=A, 1=B
self.last_positions = [] # 存储最近的位置用于检测僵局
self.game_history = [] # 存储完整游戏历史
self.move_count = 0 # 移动计数器
self.last_push_info = None # 记录上一步的推动信息
def get_state_key(self):
"""生成唯一状态标识"""
return hash(str(self.board) + str(self.current_player) + str(self.center_counts))
def is_valid_position(self, x, y):
"""检查坐标是否有效"""
return 0 <= x < self.rows and 0 <= y < self.cols
def get_adjacent_pieces(self, x, y):
"""获取相邻位置的棋子信息"""
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 右, 下, 左, 上
adjacent = []
for dx, dy in directions:
nx, ny = x + dx, y + dy
if self.is_valid_position(nx, ny):
piece = self.board[nx, ny]
if piece != '.': # 忽略空位
adjacent.append(((nx, ny), piece, (dx, dy)))
return adjacent
def can_push_special(self, x, y, direction):
"""检查特殊推动是否可行"""
dx, dy = direction
player_char = 'A' if self.current_player == 0 else 'B'
# 检查推动方向上的相邻位置
ax, ay = x + dx, y + dy
if not self.is_valid_position(ax, ay) or self.board[ax, ay] == '.':
return False
# 检查特殊规则条件
adjacent_piece = self.board[ax, ay]
if dx == 0: # 水平推动
if adjacent_piece != player_char: # 必须是自己棋子
return False
else: # 垂直推动
if adjacent_piece == player_char: # 必须是对手棋子
return False
# 检查目标位置是否为空
tx, ty = ax + dx, ay + dy
return self.is_valid_position(tx, ty) and self.board[tx, ty] == '.'
def can_push_normal(self, x, y, direction):
"""检查普通推动是否可行"""
dx, dy = direction
cx, cy = x, y
# 检查第一步是否有效
nx, ny = cx + dx, cy + dy
if not self.is_valid_position(nx, ny) or self.board[nx, ny] != '.':
return False
return True
def push_piece(self, x, y, direction, move_type):
"""执行推动操作"""
dx, dy = direction
player_char = 'A' if self.current_player == 0 else 'B'
if move_type == 'special':
# 特殊推动:移动两个棋子
ax, ay = x + dx, y + dy
tx, ty = ax + dx, ay + dy
# 移动相邻棋子
self.board[tx, ty] = self.board[ax, ay]
self.board[ax, ay] = '.'
# 移动主棋子
self.board[x + dx, y + dy] = self.board[x, y]
self.board[x, y] = '.'
elif move_type == 'normal':
# 普通推动:推动直到遇到障碍
cx, cy = x, y
while True:
nx, ny = cx + dx, cy + dy
if not self.is_valid_position(nx, ny) or self.board[nx, ny] != '.':
break
cx, cy = nx, ny
# 移动棋子到最终位置
self.board[cx, cy] = self.board[x, y]
self.board[x, y] = '.'
def find_winner(self):
"""检查胜利条件"""
if self.center_counts[0] >= self.win_count:
return 0 # A获胜
elif self.center_counts[1] >= self.win_count:
return 1 # B获胜
return -1 # 无胜者
def is_draw(self):
"""检查是否平局"""
# 检查僵局条件(连续3次重复局面)
if len(self.last_positions) >= 6:
if (self.last_positions[-1] == self.last_positions[-3] == self.last_positions[-5] and
self.last_positions[-2] == self.last_positions[-4] == self.last_positions[-6]):
return True
# 检查是否无合法移动
if not self.get_valid_moves():
self.current_player = 1 - self.current_player
if not self.get_valid_moves():
return True
self.current_player = 1 - self.current_player
return False
def get_valid_moves(self):
"""获取所有合法移动"""
valid_moves = []
player_char = 'A' if self.current_player == 0 else 'B'
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 右, 下, 左, 上
# 查找所有可能的移动
for x in range(self.rows):
for y in range(self.cols):
if self.board[x, y] == player_char:
# 检查特殊推动
for direction in directions:
if self.can_push_special(x, y, direction):
valid_moves.append((x, y, direction, 'special'))
# 检查普通推动
for direction in directions:
if self.can_push_normal(x, y, direction):
valid_moves.append((x, y, direction, 'normal'))
# 禁止反推规则
if self.last_push_info is not None:
if self.current_player != self.last_push_info['from_player']:
pushed_pos = self.last_push_info['pushed_piece_final_pos']
pushed_dir = self.last_push_info['pushed_direction']
reverse_dir = (-pushed_dir[0], -pushed_dir[1])
filtered_moves = []
for move in valid_moves:
x, y, dir, mtype = move
if mtype == 'special' and (x, y) == pushed_pos and dir == reverse_dir:
continue
filtered_moves.append(move)
valid_moves = filtered_moves
return valid_moves
def make_move(self, move=None):
"""执行移动"""
if move is None:
valid_moves = self.get_valid_moves()
if not valid_moves:
return False
move = random.choice(valid_moves)
x, y, direction, move_type = move
self.push_piece(x, y, direction, move_type)
# 检查是否有棋子到达中心点
cx, cy = self.center
if self.board[cx, cy] != '.':
if self.board[cx, cy] == 'A':
self.center_counts[0] += 1
elif self.board[cx, cy] == 'B':
self.center_counts[1] += 1
self.board[cx, cy] = '.'
# 记录上一步的推动信息(垂直特殊推动)
self.last_push_info = None
if move_type == 'special' and direction[0] != 0:
pushed_piece_final_pos = (x + 2 * direction[0], y + 2 * direction[1])
self.last_push_info = {
'pushed_piece_final_pos': pushed_piece_final_pos,
'pushed_direction': direction,
'from_player': self.current_player
}
# 记录游戏状态
self.last_positions.append(self.get_state_key())
self.game_history.append((
self.current_player,
(x, y),
direction,
move_type,
np.copy(self.board),
self.center_counts.copy()
))
# 检查游戏结束条件
winner = self.find_winner()
if winner != -1:
return winner
if self.is_draw():
return 2 # 平局
# 切换到下一个玩家
self.current_player = 1 - self.current_player
self.move_count += 1
return None
def simulate_game(self, max_moves=1000):
"""模拟完整游戏"""
self.reset_game()
result = None
for _ in range(max_moves):
result = self.make_move()
if result is not None:
break
if result is None:
return 2 # 平局
return result
def visualize_board(self, piece_colors=None):
"""可视化棋盘"""
reset_color = '\033[0m'
center_color = '\033[93m' # 中心点颜色 (黄色)
default_color = reset_color
player_char = 'A' if self.current_player == 0 else 'B'
# 显示中心点计数
print(f"中心点计数: A={self.center_counts[0]}/{self.win_count}, B={self.center_counts[1]}/{self.win_count}")
# 显示棋盘
for i, row in enumerate(self.board):
for j, cell in enumerate(row):
if cell == 'A' or cell == 'B':
if piece_colors and (i, j) in piece_colors:
color_code = piece_colors[(i, j)]
elif piece_colors is None:
color_code = '\033[91m' if cell == 'A' else '\033[94m'
elif cell == player_char:
color_code = '\033[91m' if self.current_player == 0 else '\033[94m'
else:
color_code = '\033[90m' # 对手颜色
print(f"{color_code}●{reset_color}", end=" ")
elif (i, j) == self.center:
print(f"{center_color}★{reset_color}", end=" ")
else:
print("·", end=" ")
print()
player_colors = {
0: '\033[91mA\033[0m',
1: '\033[94mB\033[0m'
}
def show_rules():
"""显示简化后的游戏规则"""
os.system('cls' if os.name == 'nt' else 'clear')
print("推推棋游戏规则")
print("=" * 40)
print("1. 目标:先将1个棋子送入中心点(★)者获胜")
print()
print("2. 棋盘布局:")
print(f" - 7行×5列棋盘,底部行是A方(红●),顶部行是B方(蓝●)") # 修改尺寸说明
print(" - 中心位置标记为★")
print()
print("3. 移动方式:")
print(" ① 普通推动:向相邻空位推自己的棋,会一直移动到遇障碍为止")
print(" ② 特殊推动:")
print(" - 水平推(左/右):只能推相邻的自己的棋,被推棋前进一格")
print(" - 垂直推(上/下):只能推相邻的对手的棋,被推棋前进一格")
print()
print("4. 特殊规则:")
print(" - 棋子进入中心点后会被移除,同时增加对应玩家计数")
print(" - 被垂直推动的棋子,下一回合不能被反方向推回")
print(" - 连续3次重复局面或双方无合法移动均判为平局")
print("=" * 40)
# 显示初始棋盘
print("\n初始棋盘状态:")
game = PushGame()
game.visualize_board()
input("\n按任意键返回主菜单...")
def interactive_game():
"""交互式游戏"""
game = PushGame()
direction_symbols = {
(0, 1): '→',
(1, 0): '↓',
(0, -1): '←',
(-1, 0): '↑'
}
# 不同棋子的颜色集合
piece_colors = [
'\033[91m', '\033[92m', '\033[93m', '\033[94m',
'\033[95m', '\033[96m', '\033[97m', '\033[31m',
'\033[32m', '\033[33m'
]
reset_color = '\033[0m'
print(f"欢迎来到推推棋游戏! 棋盘大小: {game.rows}行×{game.cols}列")
print("操作说明:")
print("- 输入移动编号选择要执行的操作")
print("- 输入 'exit' 可随时退出游戏")
print("- 移动类型:普通(→↓←↑)、特殊(会标注)")
# 显示初始棋盘
print(f"\n{game.rows}行×{game.cols}列棋盘初始状态:")
game.visualize_board()
input("\n按回车键开始游戏...")
while True: # 外层循环用于多局游戏
game = PushGame()
exit_flag = False
while True: # 内层循环用于单局游戏
# 清屏并显示棋盘
os.system('cls' if os.name == 'nt' else 'clear')
# 显示当前棋盘信息
print(f"棋盘大小: {game.rows}行×{game.cols}列 | 获胜所需中心点: {game.win_count}")
# 获取所有合法移动
valid_moves = game.get_valid_moves()
# 创建棋子颜色映射字典
color_map = {}
if valid_moves:
piece_positions = set((x, y) for x, y, _, _ in valid_moves)
sorted_pieces = sorted(piece_positions, key=lambda pos: (pos[0], pos[1]))
# 为每个可移动棋子分配颜色
for i, pos in enumerate(sorted_pieces):
color_index = i % len(piece_colors)
color_map[pos] = piece_colors[color_index]
# 显示棋盘
game.visualize_board(piece_colors=color_map)
if not valid_moves:
print("无合法移动! 游戏结束")
break
# 按棋子分组移动选项
moves_by_piece = {}
for move in valid_moves:
x, y, direction, move_type = move
piece_key = (x, y)
if piece_key not in moves_by_piece:
moves_by_piece[piece_key] = []
moves_by_piece[piece_key].append((direction, move_type))
# 显示合法移动
print("\n可移动选项:")
move_counter = 1
move_map = {}
color_index = 0
# 按位置排序棋子
sorted_pieces = sorted(moves_by_piece.keys(), key=lambda pos: (pos[0], pos[1]))
for piece in sorted_pieces:
x, y = piece
color_code = piece_colors[color_index % len(piece_colors)]
color_index += 1
# 打印棋子标题
print(f"{color_code}棋子 {color_index}{reset_color} 的移动:")
# 获取并排序该棋子的移动选项
directions = moves_by_piece[piece]
sorted_directions = sorted(directions, key=lambda d: d[0])
# 打印移动选项
for direction, move_type in sorted_directions:
dir_symbol = direction_symbols[direction]
move_type_str = "特殊" if move_type == 'special' else "普通"
move_map[move_counter] = (x, y, direction, move_type)
print(f" {color_code}{move_counter}. {dir_symbol} {move_type_str}{reset_color}")
move_counter += 1
print() # 添加空行分隔
# 获取玩家输入
player_char = 'A' if game.current_player == 0 else 'B'
player_color = '\033[91m' if game.current_player == 0 else '\033[94m'
user_input = input(
f"\n{player_color}玩家 {player_char}{reset_color} 的回合 (输入编号): ").strip().lower()
if user_input == 'exit':
exit_flag = True
break
# 处理输入
if user_input.isdigit():
move_index = int(user_input)
if move_index in move_map:
move = move_map[move_index]
else:
print(f"无效编号: {move_index}!")
time.sleep(1)
continue
else:
print("请输入有效编号!")
time.sleep(1)
continue
# 执行移动
result = game.make_move(move)
if result == 0:
os.system('cls' if os.name == 'nt' else 'clear')
game.visualize_board()
print("\033[91m玩家 A 获胜!\033[0m")
break
elif result == 1:
os.system('cls' if os.name == 'nt' else 'clear')
game.visualize_board()
print("\033[94m玩家 B 获胜!\033[0m")
break
elif result == 2:
os.system('cls' if os.name == 'nt' else 'clear')
game.visualize_board()
print("平局!")
break
if exit_flag:
break # 完全退出游戏
# 询问是否再来一局
replay = input("\n再来一局? (y/n): ").strip().lower()
if replay != 'y':
break
if __name__ == "__main__":
while True:
os.system('cls' if os.name == 'nt' else 'clear')
print("推推棋游戏")
print("1. 开始游戏")
print("2. 游戏规则")
print("3. 退出")
choice = input("\n请选择 (1-3): ").strip()
if choice == '1':
interactive_game()
elif choice == '2':
show_rules()
elif choice == '3':
print("退出程序")
break
else:
print("无效选择,请重新输入")
time.sleep(1)
哈基米'棋'?.html
严格来说不是棋,一时兴起之作,属实是哈基米视频看多了想搞个电子哈基米来哈基哈基一下。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>哈基米棋</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial Rounded MT Bold', 'Arial', sans-serif;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
color: #333;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 20px;
width: 100%;
}
h1 {
color: #ff6b6b;
font-size: 2.8rem;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
.subtitle {
color: #4a4a4a;
font-size: 1.2rem;
margin-bottom: 20px;
}
.game-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
max-width: 100%;
}
.board-container {
position: relative;
margin-bottom: 20px;
}
.turn-counter {
font-size: 1.5rem;
font-weight: bold;
color: #5D4037;
margin-bottom: 15px;
text-align: center;
background-color: rgba(255, 255, 255, 0.8);
padding: 10px 20px;
border-radius: 50px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.record-display {
font-size: 1.2rem;
font-weight: bold;
color: #4CAF50;
margin-bottom: 15px;
text-align: center;
background-color: rgba(255, 255, 255, 0.8);
padding: 8px 16px;
border-radius: 50px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.board {
display: grid;
grid-template-columns: repeat(17, 40px);
grid-template-rows: repeat(17, 40px);
gap: 1px;
background-color: #8BC34A;
border: 3px solid #5D4037;
border-radius: 5px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
overflow: hidden;
position: relative;
}
.cell {
width: 40px;
height: 40px;
background-color: #AED581;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: background-color 0.2s;
position: relative;
}
.cell:hover {
background-color: #9CCC65;
}
.cell.disabled {
cursor: not-allowed;
background-color: #CCCCCC;
}
.cell.disabled:hover {
background-color: #CCCCCC;
}
.cell.valid-cucumber {
background-color: #C8E6C9;
}
.cell.valid-cucumber:hover {
background-color: #A5D6A7;
}
.cat, .cucumber {
width: 32px;
height: 32px;
border-radius: 50%;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
transition: all 0.3s ease;
z-index: 2;
position: relative;
}
.cat {
background-image: url('cat_icon.png');
background-color: #FFC107;
box-shadow: 0 0 8px rgba(255, 193, 7, 0.7);
}
.cat.moving {
background-image: url('cat_ha.png');
animation: pulse 0.5s infinite alternate;
}
.cucumber {
background-image: url('cucumber_icon.png');
background-color: #4CAF50;
box-shadow: 0 0 5px rgba(76, 175, 80, 0.5);
}
.cucumber.red {
background-color: #F44336;
animation: fadeOut 0.5s forwards;
}
.cucumber.exploding {
background-color: #FF9800;
animation: explode 0.5s forwards;
}
.cucumber.crazy {
background-image: url('crazy.png');
background-color: #FF5722;
animation: crazy 0.8s forwards;
}
/* 箭头尾迹样式 */
.trail {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
}
.trail-arrow {
width: 0;
height: 0;
border-style: solid;
position: absolute;
}
.trail-1 {
opacity: 0.9;
}
.trail-2 {
opacity: 0.7;
}
.trail-3 {
opacity: 0.5;
}
.trail-4 {
opacity: 0.3;
}
.trail-5 {
opacity: 0.2;
}
/* 方向箭头 */
.arrow-up {
border-width: 0 8px 12px 8px;
border-color: transparent transparent #FF9800 transparent;
top: 5px;
}
.arrow-down {
border-width: 12px 8px 0 8px;
border-color: #FF9800 transparent transparent transparent;
bottom: 5px;
}
.arrow-left {
border-width: 8px 12px 8px 0;
border-color: transparent #FF9800 transparent transparent;
left: 5px;
}
.arrow-right {
border-width: 8px 0 8px 12px;
border-color: transparent transparent transparent #FF9800;
right: 5px;
}
.arrow-up-left {
border-width: 0 0 12px 12px;
border-color: transparent transparent #FF9800 transparent;
top: 5px;
left: 5px;
}
.arrow-up-right {
border-width: 0 12px 12px 0;
border-color: transparent transparent #FF9800 transparent;
top: 5px;
right: 5px;
}
.arrow-down-left {
border-width: 12px 0 0 12px;
border-color: transparent transparent transparent #FF9800;
bottom: 5px;
left: 5px;
}
.arrow-down-right {
border-width: 12px 12px 0 0;
border-color: transparent #FF9800 transparent transparent;
bottom: 5px;
right: 5px;
}
@keyframes fadeOut {
to {
opacity: 0;
transform: scale(0.5);
}
}
@keyframes explode {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.5);
opacity: 0.7;
}
100% {
transform: scale(0.5);
opacity: 0;
}
}
@keyframes crazy {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.5);
opacity: 0.7;
}
100% {
transform: scale(0.5);
opacity: 0;
}
}
@keyframes pulse {
from {
transform: scale(1);
}
to {
transform: scale(1.1);
}
}
.controls {
display: flex;
gap: 15px;
margin-bottom: 20px;
flex-wrap: wrap;
justify-content: center;
}
button {
padding: 12px 25px;
font-size: 1rem;
border: none;
border-radius: 50px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: bold;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
#start-btn {
background-color: #4CAF50;
color: white;
}
#start-btn:hover {
background-color: #45a049;
transform: translateY(-2px);
}
#start-btn:disabled {
background-color: #cccccc;
cursor: not-allowed;
transform: none;
}
#end-btn {
background-color: #f44336;
color: white;
}
#end-btn:hover {
background-color: #d32f2f;
transform: translateY(-2px);
}
#rules-btn {
background-color: #2196F3;
color: white;
}
#rules-btn:hover {
background-color: #0b7dda;
transform: translateY(-2px);
}
#record-btn {
background-color: #9C27B0;
color: white;
}
#record-btn:hover {
background-color: #7B1FA2;
transform: translateY(-2px);
}
#reset-record-btn {
background-color: #FF9800;
color: white;
}
#reset-record-btn:hover {
background-color: #F57C00;
transform: translateY(-2px);
}
#trail-toggle-btn {
background-color: #607D8B;
color: white;
}
#trail-toggle-btn:hover {
background-color: #455A64;
transform: translateY(-2px);
}
.rules-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 1000;
justify-content: center;
align-items: center;
}
.rules-content {
background-color: white;
padding: 30px;
border-radius: 15px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.3);
}
.rules-content h2 {
color: #ff6b6b;
margin-bottom: 20px;
text-align: center;
font-size: 1.8rem;
}
.rules-content h3 {
color: #4CAF50;
margin: 15px 0 10px;
}
.rules-content p, .rules-content ul {
margin-bottom: 15px;
line-height: 1.6;
}
.rules-content ul {
padding-left: 20px;
}
.rules-content li {
margin-bottom: 8px;
}
.close-btn {
display: block;
margin: 20px auto 0;
padding: 10px 25px;
background-color: #ff6b6b;
color: white;
border: none;
border-radius: 50px;
cursor: pointer;
font-weight: bold;
transition: background-color 0.3s;
}
.close-btn:hover {
background-color: #ff5252;
}
.cat-message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #ff6b6b;
padding: 5px 10px;
font-weight: bold;
font-size: 1.5rem;
opacity: 0;
transition: opacity 0.3s;
z-index: 100;
text-align: center;
pointer-events: none;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
background-color: transparent;
border-radius: 0;
box-shadow: none;
}
.cat-message.show {
opacity: 1;
}
.game-over-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 1000;
justify-content: center;
align-items: center;
}
.game-over-content {
background-color: white;
padding: 40px;
border-radius: 15px;
max-width: 500px;
width: 90%;
text-align: center;
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.3);
}
.game-over-content h2 {
color: #ff6b6b;
margin-bottom: 20px;
font-size: 2.5rem;
}
.game-over-content p {
font-size: 1.5rem;
margin-bottom: 30px;
color: #333;
}
@media (max-width: 700px) {
.board {
grid-template-columns: repeat(17, 25px);
grid-template-rows: repeat(17, 25px);
}
.cell {
width: 25px;
height: 25px;
}
.cat, .cucumber {
width: 20px;
height: 20px;
}
/* 移动端箭头大小调整 */
.arrow-up, .arrow-down {
border-width: 0 5px 8px 5px;
}
.arrow-left, .arrow-right {
border-width: 5px 8px 5px 0;
}
.arrow-up-left, .arrow-up-right, .arrow-down-left, .arrow-down-right {
border-width: 0 0 8px 8px;
}
.cat-message {
font-size: 1.2rem;
padding: 3px 8px;
}
.controls {
flex-direction: column;
align-items: center;
}
button {
width: 200px;
}
}
</style>
</head>
<body>
<div class="header">
<h1>哈基米棋</h1>
<p class="subtitle">挑战你的策略,阻止哈基米到达边界!</p>
</div>
<div class="game-container">
<div class="board-container">
<div class="turn-counter" id="turn-counter">存活回合数: 0</div>
<div class="record-display" id="record-display">最高纪录: 0 回合</div>
<div class="cat-message" id="cat-message"></div>
<div class="board" id="board"></div>
</div>
<div class="controls">
<button id="start-btn">开始游戏</button>
<button id="end-btn">结束游戏</button>
<button id="rules-btn">游戏规则</button>
<button id="record-btn">查看纪录</button>
<button id="reset-record-btn">重置纪录</button>
<button id="trail-toggle-btn">隐藏尾迹</button>
</div>
</div>
<div class="rules-modal" id="rules-modal">
<div class="rules-content">
<h2>哈基米棋游戏规则</h2>
<h3>游戏目标</h3>
<p>阻止哈基米到达棋盘边界,同时尽可能多地保持回合数。</p>
<h3>游戏规则</h3>
<ul>
<li>哈基米出生于中央!</li>
<li>你只能在哈基米周围8格内放置黄瓜!</li>
<li>如果哈基米周围有黄瓜,哈基米就会拍碎它!
<ul>
<li>黄瓜被拍散了,但是在两侧生成两个新黄瓜!</li>
<li>哈基米应激跑向向黄瓜相反的方向!</li>
</ul>
</li>
<li>哈基米直接碰到黄瓜,就会吃掉它,然后把周围的黄瓜都炸掉!</li>
<li>哈基米会持续哈气,直到周围没有一根黄瓜!</li>
<li>你只能在哈基米停止哈气后,放下一根黄瓜!</li>
</ul>
<h3>哈基米的特殊哈压库:</h3>
<ul>
<li>①连续5次,哈基米仅腾挪1格就碰上黄瓜,触发超哈气!第6次会无视黄瓜移动2格!</li>
<li>②哈基米连续3回合中,每回合都仅拍了一次黄瓜就停止移动,触发超哈气!下一回合的黄瓜会被直接吃掉!</li>
</ul>
<button class="close-btn" id="close-rules">关闭</button>
</div>
</div>
<div class="game-over-modal" id="game-over-modal">
<div class="game-over-content">
<h2>游戏结束</h2>
<p id="game-over-message">哈基米吃掉了你的叮咚鸡,你共支撑了0回合!</p>
<button class="close-btn" id="close-game-over">确定</button>
</div>
</div>
<script>
// 游戏状态变量
let gameActive = false;
let catPosition = { x: 8, y: 8 };
let cucumbers = [];
let currentTurn = 0;
let catMoveCount = 0;
let catSpeed = 500; // 初始移动速度(毫秒)
let baseSpeed = 500;
let oneMoveTriggers = 0; // 记录连续移动1格就触发黄瓜判断的次数
let consecutiveSingleHitTurns = 0; // 记录连续单拍回合数
let isCatMoving = false; // 标记哈基米是否正在移动
let catSound = new Audio('catsound.wav');
let backgroundMusic = new Audio('catmusic.wav');
backgroundMusic.loop = true;
let hitCountThisTurn = 0; // 当前回合拍黄瓜的次数
let isSpecialRule5Active = false; // 标记规则⑤是否激活
let isForceMove2 = false; // 标记是否强制移动2格
let highScore = 0; // 最高纪录
// 新增变量:跟踪连续移动1格就触发拍黄瓜的次数
let consecutiveOneMoveHits = 0;
// 新增变量:尾迹系统
let catTrail = []; // 存储哈基米移动路径和方向
let showTrail = true; // 是否显示尾迹
// DOM元素
const board = document.getElementById('board');
const turnCounter = document.getElementById('turn-counter');
const recordDisplay = document.getElementById('record-display');
const catMessage = document.getElementById('cat-message');
const startBtn = document.getElementById('start-btn');
const endBtn = document.getElementById('end-btn');
const rulesBtn = document.getElementById('rules-btn');
const recordBtn = document.getElementById('record-btn');
const resetRecordBtn = document.getElementById('reset-record-btn');
const trailToggleBtn = document.getElementById('trail-toggle-btn');
const rulesModal = document.getElementById('rules-modal');
const closeRules = document.getElementById('close-rules');
const gameOverModal = document.getElementById('game-over-modal');
const gameOverMessage = document.getElementById('game-over-message');
const closeGameOver = document.getElementById('close-game-over');
// 检查位置是否在哈基米周围
function isInCatAdjacent(x, y) {
return Math.abs(x - catPosition.x) <= 1 && Math.abs(y - catPosition.y) <= 1;
}
// 更新可放置黄瓜的格子高亮
function updateValidCucumberCells() {
// 清除所有高亮
document.querySelectorAll('.cell.valid-cucumber').forEach(cell => {
cell.classList.remove('valid-cucumber');
});
// 如果不是游戏进行中或哈基米在移动,不显示高亮
if (!gameActive || isCatMoving) return;
// 高亮显示可放置黄瓜的格子
for (let y = 0; y < 17; y++) {
for (let x = 0; x < 17; x++) {
if (isInCatAdjacent(x, y) &&
!(x === catPosition.x && y === catPosition.y) &&
!cucumbers.some(c => c.x === x && c.y === y)) {
const cell = document.querySelector(`.cell[data-x="${x}"][data-y="${y}"]`);
if (cell) {
cell.classList.add('valid-cucumber');
}
}
}
}
}
// 初始化纪录
function initializeRecord() {
// 从localStorage加载纪录
const savedRecord = localStorage.getItem('hacatmiHighScore');
if (savedRecord !== null) {
highScore = parseInt(savedRecord);
}
updateRecordDisplay();
}
// 更新纪录显示
function updateRecordDisplay() {
recordDisplay.textContent = `最高纪录: ${highScore} 回合`;
}
// 检查并更新纪录
function checkAndUpdateRecord() {
if (currentTurn > highScore) {
highScore = currentTurn;
localStorage.setItem('hacatmiHighScore', highScore);
updateRecordDisplay();
return true;
}
return false;
}
// 显示纪录信息
function showRecordInfo() {
if (highScore === 0) {
showCatMessage("暂无纪录,加油创造吧!");
} else {
showCatMessage(`当前最高纪录: ${highScore} 回合`);
}
}
// 重置纪录
function resetRecord() {
if (confirm("确定要重置最高纪录吗?")) {
highScore = 0;
localStorage.setItem('hacatmiHighScore', highScore);
updateRecordDisplay();
showCatMessage("纪录已重置");
}
}
// 切换尾迹显示
function toggleTrail() {
showTrail = !showTrail;
trailToggleBtn.textContent = showTrail ? "隐藏尾迹" : "显示尾迹";
if (!showTrail) {
clearTrail();
} else {
updateTrail();
}
}
// 添加尾迹
function addTrail(x, y, direction) {
if (!showTrail) return;
// 限制尾迹长度为5
if (catTrail.length >= 5) {
catTrail.shift();
}
// 添加新位置和方向到尾迹
catTrail.push({ x, y, direction });
// 更新尾迹显示
updateTrail();
}
// 获取箭头方向类名
function getArrowClass(dx, dy) {
if (dx === 0 && dy === -1) return 'arrow-up';
if (dx === 0 && dy === 1) return 'arrow-down';
if (dx === -1 && dy === 0) return 'arrow-left';
if (dx === 1 && dy === 0) return 'arrow-right';
if (dx === 1 && dy === -1) return 'arrow-up-right';
if (dx === 1 && dy === 1) return 'arrow-down-right';
if (dx === -1 && dy === -1) return 'arrow-up-left';
if (dx === -1 && dy === 1) return 'arrow-down-left';
return 'arrow-up'; // 默认
}
// 更新尾迹显示
function updateTrail() {
if (!showTrail) return;
// 清除所有尾迹
document.querySelectorAll('.trail').forEach(trail => {
trail.remove();
});
// 添加尾迹
catTrail.forEach((pos, index) => {
const cell = document.querySelector(`.cell[data-x="${pos.x}"][data-y="${pos.y}"]`);
if (cell) {
const trail = document.createElement('div');
trail.className = `trail trail-${index + 1}`;
const arrow = document.createElement('div');
arrow.className = `trail-arrow ${getArrowClass(pos.direction.dx, pos.direction.dy)}`;
trail.appendChild(arrow);
cell.appendChild(trail);
}
});
}
// 清除尾迹
function clearTrail() {
catTrail = [];
document.querySelectorAll('.trail').forEach(trail => {
trail.remove();
});
}
// 初始化棋盘
function initializeBoard() {
board.innerHTML = '';
for (let y = 0; y < 17; y++) {
for (let x = 0; x < 17; x++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.x = x;
cell.dataset.y = y;
// 放置哈基米
if (x === catPosition.x && y === catPosition.y) {
const cat = document.createElement('div');
cat.className = 'cat';
cell.appendChild(cat);
}
cell.addEventListener('click', () => placeCucumber(x, y));
board.appendChild(cell);
}
}
// 更新可放置黄瓜的格子
updateValidCucumberCells();
}
// 放置黄瓜
function placeCucumber(x, y) {
if (!gameActive || isCatMoving) return;
// 检查是否在哈基米周围
if (!isInCatAdjacent(x, y)) {
showCatMessage("只能放在哈基米周围!");
return;
}
// 检查位置是否有效
if (x === catPosition.x && y === catPosition.y) {
showCatMessage("不能放在哈基米的位置!");
return;
}
// 检查是否已有黄瓜
if (cucumbers.some(c => c.x === x && c.y === y)) {
showCatMessage("这里已经有黄瓜了!");
return;
}
// 清除尾迹
clearTrail();
// 重置当前回合状态
hitCountThisTurn = 0;
// 添加黄瓜
cucumbers.push({ x, y });
updateBoard();
// 黄瓜回合结束,哈基米行动
currentTurn++;
updateTurnCounter();
// 检查黄瓜是否在哈基米周围
const isAdjacent = Math.abs(x - catPosition.x) <= 1 && Math.abs(y - catPosition.y) <= 1;
// 检查是否触发连续单拍特殊规则(规则⑤)
if (isAdjacent && consecutiveSingleHitTurns >= 3) {
// 触发特殊规则:直接吃掉黄瓜
isSpecialRule5Active = true;
showCatMessage("别哈把我基米住!");
playCatSound();
// 移除黄瓜
cucumbers = cucumbers.filter(c => !(c.x === x && c.y === y));
updateBoard();
// 吃掉黄瓜,触发规则④
eatCucumberAndMove(x, y);
return;
}
if (isAdjacent) {
// 黄瓜在哈基米周围
hitCountThisTurn++;
showCatMessage("哈!");
playCatSound();
// 黄瓜变红并消失
const cucumberCell = document.querySelector(`.cell[data-x="${x}"][data-y="${y}"] .cucumber`);
cucumberCell.classList.add('red');
// 计算分裂方向
const dx = x - catPosition.x;
const dy = y - catPosition.y;
// 移除原黄瓜
setTimeout(() => {
cucumbers = cucumbers.filter(c => !(c.x === x && c.y === y));
// 计算分裂位置 - 修正分裂逻辑
let splitPositions = [];
if (dx === 0) {
// 垂直方向:分裂到左右
splitPositions.push({ x: x - 1, y: y });
splitPositions.push({ x: x + 1, y: y });
} else if (dy === 0) {
// 水平方向:分裂到上下
splitPositions.push({ x: x, y: y - 1 });
splitPositions.push({ x: x, y: y + 1 });
} else {
// 对角线方向:分裂到垂直两侧
splitPositions.push({ x: x - dy, y: y + dx });
splitPositions.push({ x: x + dy, y: y - dx });
}
// 添加分裂的黄瓜(如果在棋盘内且没有其他黄瓜)
splitPositions.forEach(pos => {
if (isValidPosition(pos.x, pos.y) && !cucumbers.some(c => c.x === pos.x && c.y === pos.y)) {
cucumbers.push(pos);
}
});
// 检查是否触发特殊规则①
if (consecutiveOneMoveHits >= 5) {
// 触发特殊规则①:强制移动2格
showCatMessage("别哈把我基米住!");
isForceMove2 = true;
consecutiveOneMoveHits = 0;
// 强制移动2格,一格一格移动
moveCat(-dx, -dy, 2, true);
} else {
// 正常移动 - 向相反方向移动2格
moveCat(-dx, -dy, 2);
}
updateBoard();
}, 500);
} else {
// 黄瓜不在哈基米周围,哈基米向最近边界移动
moveToNearestBoundary();
}
}
// 吃掉黄瓜并炸掉周围的黄瓜(修改后的规则)
function eatCucumberAndMove(x, y) {
isCatMoving = true;
disableBoard();
// 显示爆炸消息
showCatMessage("轰!黄瓜爆炸了!");
// 收集要炸掉的黄瓜(包括被吃的黄瓜和周围的黄瓜)
const cucumbersToExplode = [];
// 添加被吃的黄瓜
cucumbersToExplode.push({ x, y });
// 添加周围8格内的黄瓜
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
if (dx === 0 && dy === 0) continue; // 跳过自己
const nx = x + dx;
const ny = y + dy;
if (isValidPosition(nx, ny)) {
// 检查这个位置是否有黄瓜
const cucumber = cucumbers.find(c => c.x === nx && c.y === ny);
if (cucumber) {
cucumbersToExplode.push(cucumber);
}
}
}
}
// 为每个被炸的黄瓜播放音效和动画
cucumbersToExplode.forEach((cucumber, index) => {
setTimeout(() => {
// 播放爆炸音效
const bombSound = new Audio('bomb.wav');
bombSound.play().catch(e => console.log("爆炸音效播放失败:", e));
// 替换为crazy图标
const cucumberCell = document.querySelector(`.cell[data-x="${cucumber.x}"][data-y="${cucumber.y}"] .cucumber`);
if (cucumberCell) {
cucumberCell.classList.add('crazy');
}
}, index * 100); // 每个黄瓜间隔100毫秒播放音效和动画
});
// 移除所有被炸的黄瓜
setTimeout(() => {
cucumbers = cucumbers.filter(c =>
!cucumbersToExplode.some(explodeCucumber =>
explodeCucumber.x === c.x && explodeCucumber.y === c.y
)
);
// 移动哈基米到黄瓜位置
catPosition.x = x;
catPosition.y = y;
updateBoard();
// 强制向最近边界移动2格
setTimeout(() => {
moveToNearestBoundary(true); // true表示这是吃掉黄瓜后的强制移动
}, 500);
}, cucumbersToExplode.length * 100 + 300); // 等待所有音效和动画播放完毕
}
// 检查位置是否有效
function isValidPosition(x, y) {
return x >= 0 && x < 17 && y >= 0 && y < 17;
}
// 哈基米移动 - 修改为每移动一格就进行判定
function moveCat(dx, dy, steps, isForceMove = false) {
if (!gameActive) return;
isCatMoving = true;
disableBoard();
// 设置哈基米为移动状态
updateCatImage(true);
let remainingSteps = steps;
let currentStep = 0;
const moveStep = () => {
if (remainingSteps <= 0) {
// 移动完成,检查是否继续行动
checkCucumberAfterMove();
return;
}
// 如果是强制移动2格,则进行特殊处理
if (isForceMove2 && remainingSteps === steps) {
// 强制移动2格,每格都进行判定但不中断移动
const stepDx = dx;
const stepDy = dy;
// 第一步移动
const newX1 = catPosition.x + stepDx;
const newY1 = catPosition.y + stepDy;
// 检查第一步是否到达边界
if (!isValidPosition(newX1, newY1)) {
gameOver("哈基米吃掉了你的叮咚鸡,你共支撑了" + currentTurn + "回合!");
return;
}
// 添加尾迹
addTrail(catPosition.x, catPosition.y, { dx: stepDx, dy: stepDy });
// 移动哈基米到第一步位置
catPosition.x = newX1;
catPosition.y = newY1;
// 更新棋盘
updateBoard();
// 检查第一步是否碰到黄瓜
const cucumberIndex1 = cucumbers.findIndex(c => c.x === newX1 && c.y === newY1);
if (cucumberIndex1 !== -1) {
// 吃掉黄瓜并触发爆炸(但不中断移动)
const eatenCucumber = cucumbers[cucumberIndex1];
// 显示爆炸消息
showCatMessage("轰!黄瓜爆炸了!");
// 收集要炸掉的黄瓜(包括被吃的黄瓜和周围的黄瓜)
const cucumbersToExplode = [];
// 添加被吃的黄瓜
cucumbersToExplode.push(eatenCucumber);
// 添加周围8格内的黄瓜
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
if (dx === 0 && dy === 0) continue; // 跳过自己
const nx = eatenCucumber.x + dx;
const ny = eatenCucumber.y + dy;
if (isValidPosition(nx, ny)) {
// 检查这个位置是否有黄瓜
const cucumber = cucumbers.find(c => c.x === nx && c.y === ny);
if (cucumber) {
cucumbersToExplode.push(cucumber);
}
}
}
}
// 为每个被炸的黄瓜播放音效和动画
cucumbersToExplode.forEach((cucumber, index) => {
setTimeout(() => {
// 播放爆炸音效
const bombSound = new Audio('bomb.wav');
bombSound.play().catch(e => console.log("爆炸音效播放失败:", e));
// 替换为crazy图标
const cucumberCell = document.querySelector(`.cell[data-x="${cucumber.x}"][data-y="${cucumber.y}"] .cucumber`);
if (cucumberCell) {
cucumberCell.classList.add('crazy');
}
}, index * 100);
});
// 移除所有被炸的黄瓜
setTimeout(() => {
cucumbers = cucumbers.filter(c =>
!cucumbersToExplode.some(explodeCucumber =>
explodeCucumber.x === c.x && explodeCucumber.y === c.y
)
);
updateBoard();
}, cucumbersToExplode.length * 100 + 300);
}
remainingSteps--;
currentStep++;
// 第二步移动
setTimeout(() => {
const newX2 = catPosition.x + stepDx;
const newY2 = catPosition.y + stepDy;
// 检查第二步是否到达边界
if (!isValidPosition(newX2, newY2)) {
gameOver("哈基米吃掉了你的叮咚鸡,你共支撑了" + currentTurn + "回合!");
return;
}
// 添加尾迹
addTrail(catPosition.x, catPosition.y, { dx: stepDx, dy: stepDy });
// 移动哈基米到第二步位置
catPosition.x = newX2;
catPosition.y = newY2;
// 更新棋盘
updateBoard();
// 检查第二步是否碰到黄瓜
const cucumberIndex2 = cucumbers.findIndex(c => c.x === newX2 && c.y === newY2);
if (cucumberIndex2 !== -1) {
// 吃掉黄瓜并触发爆炸(但不中断移动)
const eatenCucumber = cucumbers[cucumberIndex2];
// 显示爆炸消息
showCatMessage("轰!黄瓜爆炸了!");
// 收集要炸掉的黄瓜(包括被吃的黄瓜和周围的黄瓜)
const cucumbersToExplode = [];
// 添加被吃的黄瓜
cucumbersToExplode.push(eatenCucumber);
// 添加周围8格内的黄瓜
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
if (dx === 0 && dy === 0) continue; // 跳过自己
const nx = eatenCucumber.x + dx;
const ny = eatenCucumber.y + dy;
if (isValidPosition(nx, ny)) {
// 检查这个位置是否有黄瓜
const cucumber = cucumbers.find(c => c.x === nx && c.y === ny);
if (cucumber) {
cucumbersToExplode.push(cucumber);
}
}
}
}
// 为每个被炸的黄瓜播放音效和动画
cucumbersToExplode.forEach((cucumber, index) => {
setTimeout(() => {
// 播放爆炸音效
const bombSound = new Audio('bomb.wav');
bombSound.play().catch(e => console.log("爆炸音效播放失败:", e));
// 替换为crazy图标
const cucumberCell = document.querySelector(`.cell[data-x="${cucumber.x}"][data-y="${cucumber.y}"] .cucumber`);
if (cucumberCell) {
cucumberCell.classList.add('crazy');
}
}, index * 100);
});
// 移除所有被炸的黄瓜
setTimeout(() => {
cucumbers = cucumbers.filter(c =>
!cucumbersToExplode.some(explodeCucumber =>
explodeCucumber.x === c.x && explodeCucumber.y === c.y
)
);
updateBoard();
// 完成强制移动后,重置标志并检查周围黄瓜
isForceMove2 = false;
remainingSteps = 0;
setTimeout(() => {
checkCucumberAfterMove();
}, 500);
}, cucumbersToExplode.length * 100 + 300);
} else {
// 完成强制移动后,重置标志并检查周围黄瓜
isForceMove2 = false;
remainingSteps = 0;
setTimeout(() => {
checkCucumberAfterMove();
}, catSpeed);
}
}, catSpeed);
return;
} else {
// 正常移动,每移动一格都进行判定
const newX = catPosition.x + dx;
const newY = catPosition.y + dy;
// 检查是否到达边界
if (!isValidPosition(newX, newY)) {
gameOver("哈基米吃掉了你的叮咚鸡,你共支撑了" + currentTurn + "回合!");
return;
}
// 添加尾迹
addTrail(catPosition.x, catPosition.y, { dx, dy });
// 移动哈基米
catPosition.x = newX;
catPosition.y = newY;
// 更新棋盘
updateBoard();
// 检查是否碰到黄瓜(规则④ - 修改为炸掉周围的黄瓜)
const cucumberIndex = cucumbers.findIndex(c => c.x === newX && c.y === newY);
if (cucumberIndex !== -1) {
// 吃掉黄瓜并炸掉周围的黄瓜
const eatenCucumber = cucumbers[cucumberIndex];
// 显示爆炸消息
showCatMessage("轰!黄瓜爆炸了!");
// 收集要炸掉的黄瓜(包括被吃的黄瓜和周围的黄瓜)
const cucumbersToExplode = [];
// 添加被吃的黄瓜
cucumbersToExplode.push(eatenCucumber);
// 添加周围8格内的黄瓜
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
if (dx === 0 && dy === 0) continue; // 跳过自己
const nx = eatenCucumber.x + dx;
const ny = eatenCucumber.y + dy;
if (isValidPosition(nx, ny)) {
// 检查这个位置是否有黄瓜
const cucumber = cucumbers.find(c => c.x === nx && c.y === ny);
if (cucumber) {
cucumbersToExplode.push(cucumber);
}
}
}
}
// 为每个被炸的黄瓜播放音效和动画
cucumbersToExplode.forEach((cucumber, index) => {
setTimeout(() => {
// 播放爆炸音效
const bombSound = new Audio('bomb.wav');
bombSound.play().catch(e => console.log("爆炸音效播放失败:", e));
// 替换为crazy图标
const cucumberCell = document.querySelector(`.cell[data-x="${cucumber.x}"][data-y="${cucumber.y}"] .cucumber`);
if (cucumberCell) {
cucumberCell.classList.add('crazy');
}
}, index * 100);
});
// 移除所有被炸的黄瓜
setTimeout(() => {
cucumbers = cucumbers.filter(c =>
!cucumbersToExplode.some(explodeCucumber =>
explodeCucumber.x === c.x && explodeCucumber.y === c.y
)
);
updateBoard();
// 强制向最近边界移动2格
moveToNearestBoundary(true);
}, cucumbersToExplode.length * 100 + 300);
return;
}
remainingSteps--;
currentStep++;
}
// 调整速度 - 移动越来越快(增强加速效果)
// 从500ms开始,每步减少80ms,最低50ms
catSpeed = Math.max(50, baseSpeed - (currentStep * 80));
// 检查是否需要立即判定周围黄瓜
if (remainingSteps > 0 && !isForceMove2) {
// 检查哈基米周围是否有黄瓜
const adjacentCucumbers = cucumbers.filter(c =>
Math.abs(c.x - catPosition.x) <= 1 && Math.abs(c.y - catPosition.y) <= 1
);
if (adjacentCucumbers.length > 0) {
// 有黄瓜在周围,立即进行拍黄瓜判定
const selectedCucumber = selectOptimalCucumber(adjacentCucumbers);
if (selectedCucumber) {
// 记录连续移动1格就触发拍黄瓜
consecutiveOneMoveHits++;
hitCountThisTurn++;
const dx = selectedCucumber.x - catPosition.x;
const dy = selectedCucumber.y - catPosition.y;
showCatMessage("哈!");
playCatSound();
// 黄瓜变红并消失
const cucumberCell = document.querySelector(`.cell[data-x="${selectedCucumber.x}"][data-y="${selectedCucumber.y}"] .cucumber`);
cucumberCell.classList.add('red');
// 移除原黄瓜
setTimeout(() => {
cucumbers = cucumbers.filter(c => !(c.x === selectedCucumber.x && c.y === selectedCucumber.y));
// 计算分裂位置 - 修正分裂逻辑
let splitPositions = [];
if (dx === 0) {
// 垂直方向:分裂到左右
splitPositions.push({ x: selectedCucumber.x - 1, y: selectedCucumber.y });
splitPositions.push({ x: selectedCucumber.x + 1, y: selectedCucumber.y });
} else if (dy === 0) {
// 水平方向:分裂到上下
splitPositions.push({ x: selectedCucumber.x, y: selectedCucumber.y - 1 });
splitPositions.push({ x: selectedCucumber.x, y: selectedCucumber.y + 1 });
} else {
// 对角线方向:分裂到垂直两侧
splitPositions.push({ x: selectedCucumber.x - dy, y: selectedCucumber.y + dx });
splitPositions.push({ x: selectedCucumber.x + dy, y: selectedCucumber.y - dx });
}
// 添加分裂的黄瓜(如果在棋盘内且没有其他黄瓜)
splitPositions.forEach(pos => {
if (isValidPosition(pos.x, pos.y) && !cucumbers.some(c => c.x === pos.x && c.y === pos.y)) {
cucumbers.push(pos);
}
});
// 检查是否触发特殊规则①
if (consecutiveOneMoveHits >= 5) {
// 触发特殊规则①:强制移动2格
showCatMessage("别哈把我基米住!");
isForceMove2 = true;
consecutiveOneMoveHits = 0;
// 强制移动2格,进行特殊处理
moveCat(-dx, -dy, 2, true);
} else {
// 哈基米移动
moveCat(-dx, -dy, 2);
}
updateBoard();
}, 500);
return;
}
} else {
// 没有触发拍黄瓜,重置连续移动1格计数
consecutiveOneMoveHits = 0;
}
}
// 继续移动
setTimeout(moveStep, catSpeed);
};
moveStep();
}
// 向最近边界移动
function moveToNearestBoundary(isEatingMove = false) {
// 计算到各边界的距离
const distances = {
top: catPosition.y,
bottom: 16 - catPosition.y,
left: catPosition.x,
right: 16 - catPosition.x
};
// 找到最近边界
const minDistance = Math.min(...Object.values(distances));
const nearestBoundaries = Object.keys(distances).filter(b => distances[b] === minDistance);
// 随机选择一个最近边界
const randomBoundary = nearestBoundaries[Math.floor(Math.random() * nearestBoundaries.length)];
let dx = 0, dy = 0;
switch (randomBoundary) {
case 'top': dy = -1; break;
case 'bottom': dy = 1; break;
case 'left': dx = -1; break;
case 'right': dx = 1; break;
}
// 检查特殊规则
let steps = 2;
if (isEatingMove) {
steps = 2; // 规则④:强制移动2格
}
moveCat(dx, dy, steps, isEatingMove);
}
// 移动后检查黄瓜
function checkCucumberAfterMove() {
// 检查哈基米周围是否有黄瓜
const adjacentCucumbers = cucumbers.filter(c =>
Math.abs(c.x - catPosition.x) <= 1 && Math.abs(c.y - catPosition.y) <= 1
);
if (adjacentCucumbers.length > 0) {
// 有黄瓜在周围,继续行动
// 选择最有利于到达边界的黄瓜
const selectedCucumber = selectOptimalCucumber(adjacentCucumbers);
if (selectedCucumber) {
// 记录连续移动1格就触发拍黄瓜
consecutiveOneMoveHits++;
hitCountThisTurn++;
const dx = selectedCucumber.x - catPosition.x;
const dy = selectedCucumber.y - catPosition.y;
showCatMessage("哈!");
playCatSound();
// 黄瓜变红并消失
const cucumberCell = document.querySelector(`.cell[data-x="${selectedCucumber.x}"][data-y="${selectedCucumber.y}"] .cucumber`);
cucumberCell.classList.add('red');
// 移除原黄瓜
setTimeout(() => {
cucumbers = cucumbers.filter(c => !(c.x === selectedCucumber.x && c.y === selectedCucumber.y));
// 计算分裂位置 - 修正分裂逻辑
let splitPositions = [];
if (dx === 0) {
// 垂直方向:分裂到左右
splitPositions.push({ x: selectedCucumber.x - 1, y: selectedCucumber.y });
splitPositions.push({ x: selectedCucumber.x + 1, y: selectedCucumber.y });
} else if (dy === 0) {
// 水平方向:分裂到上下
splitPositions.push({ x: selectedCucumber.x, y: selectedCucumber.y - 1 });
splitPositions.push({ x: selectedCucumber.x, y: selectedCucumber.y + 1 });
} else {
// 对角线方向:分裂到垂直两侧
splitPositions.push({ x: selectedCucumber.x - dy, y: selectedCucumber.y + dx });
splitPositions.push({ x: selectedCucumber.x + dy, y: selectedCucumber.y - dx });
}
// 添加分裂的黄瓜(如果在棋盘内且没有其他黄瓜)
splitPositions.forEach(pos => {
if (isValidPosition(pos.x, pos.y) && !cucumbers.some(c => c.x === pos.x && c.y === pos.y)) {
cucumbers.push(pos);
}
});
// 检查是否触发特殊规则①
if (consecutiveOneMoveHits >= 5) {
// 触发特殊规则①:强制移动2格
showCatMessage("别哈把我基米住!");
isForceMove2 = true;
consecutiveOneMoveHits = 0;
// 强制移动2格,一格一格移动
moveCat(-dx, -dy, 2, true);
} else {
// 哈基米移动
moveCat(-dx, -dy, 2);
}
updateBoard();
}, 500);
}
} else {
// 周围没有黄瓜,停止行动
catSpeed = baseSpeed; // 重置速度
// 重置连续移动1格计数
consecutiveOneMoveHits = 0;
// 更新连续单拍回合计数(规则⑤)
if (hitCountThisTurn === 1) {
consecutiveSingleHitTurns++;
if (consecutiveSingleHitTurns >= 3) {
showCatMessage("警告:连续单拍3回合!");
}
} else {
consecutiveSingleHitTurns = 0;
}
// 规则⑤触发后,在停止行动后重置规则⑤状态
if (isSpecialRule5Active) {
isSpecialRule5Active = false;
consecutiveSingleHitTurns = 0; // 重置连续单拍计数
}
// 允许玩家放置黄瓜
isCatMoving = false;
// 设置哈基米为静止状态
updateCatImage(false);
enableBoard();
// 更新可放置黄瓜的格子
updateValidCucumberCells();
// 清除尾迹
clearTrail();
}
}
// 更新哈基米图片
function updateCatImage(isMoving) {
const catCell = document.querySelector(`.cell[data-x="${catPosition.x}"][data-y="${catPosition.y}"] .cat`);
if (catCell) {
if (isMoving) {
catCell.classList.add('moving');
} else {
catCell.classList.remove('moving');
}
}
}
// 选择最有利于到达边界的黄瓜
function selectOptimalCucumber(adjacentCucumbers) {
// 简化实现:随机选择一个
return adjacentCucumbers[Math.floor(Math.random() * adjacentCucumbers.length)];
}
// 更新棋盘显示
function updateBoard() {
// 清除所有单元格内容
document.querySelectorAll('.cell').forEach(cell => {
// 保留尾迹元素
const trails = cell.querySelectorAll('.trail');
cell.innerHTML = '';
trails.forEach(trail => cell.appendChild(trail));
});
// 放置哈基米
const catCell = document.querySelector(`.cell[data-x="${catPosition.x}"][data-y="${catPosition.y}"]`);
const cat = document.createElement('div');
cat.className = 'cat';
if (isCatMoving) {
cat.classList.add('moving');
}
catCell.appendChild(cat);
// 放置黄瓜
cucumbers.forEach(cucumber => {
const cucumberCell = document.querySelector(`.cell[data-x="${cucumber.x}"][data-y="${cucumber.y}"]`);
const cucumberElement = document.createElement('div');
cucumberElement.className = 'cucumber';
cucumberCell.appendChild(cucumberElement);
});
// 更新可放置黄瓜的格子
updateValidCucumberCells();
}
// 更新回合计数器
function updateTurnCounter() {
turnCounter.textContent = `存活回合数: ${currentTurn}`;
}
// 禁用棋盘(哈基米移动时)
function disableBoard() {
document.querySelectorAll('.cell').forEach(cell => {
cell.classList.add('disabled');
cell.classList.remove('valid-cucumber');
});
}
// 启用棋盘(哈基米停止移动时)
function enableBoard() {
document.querySelectorAll('.cell').forEach(cell => {
cell.classList.remove('disabled');
});
// 更新可放置黄瓜的格子
updateValidCucumberCells();
}
// 显示哈基米消息
function showCatMessage(message) {
catMessage.textContent = message;
catMessage.classList.add('show');
setTimeout(() => {
catMessage.classList.remove('show');
}, 2000);
}
// 播放哈基米声音
function playCatSound() {
catSound.currentTime = 0;
catSound.play().catch(e => console.log("音频播放失败:", e));
}
// 游戏结束
function gameOver(message) {
gameActive = false;
gameOverMessage.textContent = message;
backgroundMusic.pause();
showCatMessage("游戏结束!");
isCatMoving = false;
// 设置哈基米为静止状态
updateCatImage(false);
disableBoard();
// 检查并更新纪录
const isNewRecord = checkAndUpdateRecord();
if (isNewRecord) {
setTimeout(() => {
showCatMessage("恭喜!新纪录诞生!");
}, 1500);
}
// 清除尾迹
clearTrail();
// 显示游戏结束弹窗
setTimeout(() => {
gameOverModal.style.display = 'flex';
}, 1000);
}
// 开始游戏
function startGame() {
if (gameActive) return;
// 重置游戏状态
gameActive = true;
catPosition = { x: 8, y: 8 };
cucumbers = [];
currentTurn = 0;
catMoveCount = 0;
catSpeed = baseSpeed;
oneMoveTriggers = 0;
consecutiveSingleHitTurns = 0;
consecutiveOneMoveHits = 0; // 重置连续移动1格计数
isCatMoving = false;
hitCountThisTurn = 0;
isSpecialRule5Active = false;
isForceMove2 = false;
// 清除尾迹
clearTrail();
// 初始化棋盘
initializeBoard();
updateTurnCounter();
// 播放背景音乐
backgroundMusic.play().catch(e => console.log("背景音乐播放失败:", e));
showCatMessage("游戏开始!黄瓜先手");
}
// 结束游戏
function endGame() {
gameActive = false;
backgroundMusic.pause();
showCatMessage("游戏已结束");
isCatMoving = false;
// 设置哈基米为静止状态
updateCatImage(false);
disableBoard();
// 清除尾迹
clearTrail();
}
// 显示规则
function showRules() {
rulesModal.style.display = 'flex';
}
// 隐藏规则
function hideRules() {
rulesModal.style.display = 'none';
}
// 隐藏游戏结束弹窗
function hideGameOver() {
gameOverModal.style.display = 'none';
}
// 事件监听
startBtn.addEventListener('click', startGame);
endBtn.addEventListener('click', endGame);
rulesBtn.addEventListener('click', showRules);
recordBtn.addEventListener('click', showRecordInfo);
resetRecordBtn.addEventListener('click', resetRecord);
trailToggleBtn.addEventListener('click', toggleTrail);
closeRules.addEventListener('click', hideRules);
closeGameOver.addEventListener('click', hideGameOver);
// 初始化
initializeBoard();
initializeRecord();
</script>
</body>
</html>
1683

被折叠的 条评论
为什么被折叠?



