zylyzghc/script.js

601 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 卡片类
class Card {
constructor(suit, rank, value) {
this.suit = suit; // 花色spades, hearts, clubs, diamonds, joker
this.rank = rank; // 点数A, 2, 3, ..., 10, J, Q, K, 小王, 大王
this.value = value; // 牌值:用于比较大小
this.selected = false;
}
// 获取卡片的HTML表示
getHTML(selected = false) {
const cardDiv = document.createElement('div');
cardDiv.className = `card ${this.suit} ${selected ? 'selected' : ''}`;
cardDiv.dataset.suit = this.suit;
cardDiv.dataset.rank = this.rank;
cardDiv.dataset.value = this.value;
// 花色符号
const suitSymbol = {
spades: '♠',
hearts: '♥',
clubs: '♣',
diamonds: '♦'
}[this.suit] || this.rank;
// 创建卡片内容
const rankTop = document.createElement('div');
rankTop.className = 'rank-top';
rankTop.textContent = this.rank;
const suitMiddle = document.createElement('div');
suitMiddle.className = 'suit-middle';
suitMiddle.textContent = suitSymbol;
const rankBottom = document.createElement('div');
rankBottom.className = 'rank-bottom';
rankBottom.textContent = this.rank;
// 组装卡片
cardDiv.appendChild(rankTop);
cardDiv.appendChild(suitMiddle);
cardDiv.appendChild(rankBottom);
return cardDiv;
}
// 获取背面HTML
static getBackHTML() {
const cardDiv = document.createElement('div');
cardDiv.className = 'card card-back';
return cardDiv;
}
}
// 游戏类
class LandlordGame {
constructor() {
this.players = [
{ id: 0, name: '我', cards: [], role: '', lastPlay: [] },
{ id: 1, name: '电脑玩家1', cards: [], role: '', lastPlay: [] },
{ id: 2, name: '电脑玩家2', cards: [], role: '', lastPlay: [] }
];
this.deck = [];
this.landlordCards = [];
this.currentPlayer = 0;
this.gameStatus = 'waiting'; // waiting, calling, playing, ended
this.landlord = -1;
this.lastPlayPlayer = -1;
this.lastPlayCards = [];
this.score = 0;
this.callLandlordCount = 0;
this.initializeElements();
this.initializeEventListeners();
this.initializeDeck();
}
// 初始化DOM元素
initializeElements() {
this.elements = {
startGame: document.getElementById('start-game'),
gameStatus: document.getElementById('game-status'),
currentPlayer: document.getElementById('current-player'),
landlordCardsArea: document.getElementById('landlord-cards-area'),
landlordButtons: document.querySelector('.landlord-buttons'),
callLandlord: document.getElementById('call-landlord'),
noCall: document.getElementById('no-call'),
playButtons: document.querySelector('.play-buttons'),
playCards: document.getElementById('play-cards'),
pass: document.getElementById('pass'),
currentPlayCards: document.getElementById('current-play-cards'),
currentPlayInfo: document.getElementById('current-play-info'),
playerCards: [
document.getElementById('self-cards'),
document.getElementById('player-1-cards'),
document.getElementById('player-2-cards')
],
playerCardsCount: [
document.getElementById('self-cards-count'),
document.getElementById('player-1-cards-count'),
document.getElementById('player-2-cards-count')
],
playerRoles: [
document.getElementById('self-role'),
document.getElementById('player-1-role'),
document.getElementById('player-2-role')
],
lastPlay: [
document.getElementById('self-last-play'),
document.getElementById('player-1-last-play'),
document.getElementById('player-2-last-play')
],
score: document.getElementById('score')
};
}
// 初始化事件监听器
initializeEventListeners() {
this.elements.startGame.addEventListener('click', () => this.startGame());
this.elements.callLandlord.addEventListener('click', () => this.callLandlord());
this.elements.noCall.addEventListener('click', () => this.noCall());
this.elements.playCards.addEventListener('click', () => this.playSelectedCards());
this.elements.pass.addEventListener('click', () => this.pass());
}
// 初始化牌组
initializeDeck() {
this.deck = [];
const suits = ['spades', 'hearts', 'clubs', 'diamonds'];
const ranks = ['3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A', '2'];
const values = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
// 添加普通牌
for (let i = 0; i < ranks.length; i++) {
for (const suit of suits) {
this.deck.push(new Card(suit, ranks[i], values[i]));
}
}
// 添加大小王
this.deck.push(new Card('joker', '小王', 16));
this.deck.push(new Card('joker', '大王', 17));
}
// 洗牌算法
shuffleDeck() {
for (let i = this.deck.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[this.deck[i], this.deck[j]] = [this.deck[j], this.deck[i]];
}
}
// 开始游戏
startGame() {
this.gameStatus = 'calling';
this.currentPlayer = 0;
this.landlord = -1;
this.lastPlayPlayer = -1;
this.lastPlayCards = [];
this.callLandlordCount = 0;
// 重置玩家数据
for (const player of this.players) {
player.cards = [];
player.role = '';
player.lastPlay = [];
}
// 洗牌发牌
this.initializeDeck();
this.shuffleDeck();
this.dealCards();
// 更新UI
this.updateGameStatus('开始叫地主');
this.updateCurrentPlayer();
this.renderAllCards();
this.elements.startGame.style.display = 'none';
this.elements.landlordButtons.style.display = 'flex';
this.elements.playButtons.style.display = 'none';
this.clearCurrentPlay();
}
// 发牌
dealCards() {
// 发牌给三个玩家
for (let i = 0; i < 17; i++) {
this.players[0].cards.push(this.deck.pop());
this.players[1].cards.push(this.deck.pop());
this.players[2].cards.push(this.deck.pop());
}
// 剩余的3张作为地主牌
this.landlordCards = [...this.deck];
this.deck = [];
// 排序玩家的牌
for (const player of this.players) {
this.sortCards(player.cards);
}
}
// 排序卡片
sortCards(cards) {
cards.sort((a, b) => a.value - b.value);
}
// 叫地主
callLandlord() {
this.landlord = this.currentPlayer;
this.players[this.currentPlayer].role = '地主';
this.players[(this.currentPlayer + 1) % 3].role = '农民';
this.players[(this.currentPlayer + 2) % 3].role = '农民';
// 将地主牌加入地主手中
this.players[this.currentPlayer].cards.push(...this.landlordCards);
this.sortCards(this.players[this.currentPlayer].cards);
// 开始游戏
this.gameStatus = 'playing';
this.updateGameStatus('游戏开始,地主出牌');
this.updatePlayerRoles();
this.renderAllCards();
this.elements.landlordButtons.style.display = 'none';
this.elements.playButtons.style.display = 'flex';
// 如果是电脑地主,自动出牌
if (this.currentPlayer !== 0) {
setTimeout(() => this.computerPlay(), 1000);
}
}
// 不叫地主
noCall() {
this.callLandlordCount++;
this.currentPlayer = (this.currentPlayer + 1) % 3;
this.updateCurrentPlayer();
// 如果三个玩家都不叫,重新开始
if (this.callLandlordCount === 3) {
this.updateGameStatus('无人叫地主,重新开始');
setTimeout(() => this.startGame(), 1500);
return;
}
// 如果是电脑玩家,自动选择
if (this.currentPlayer !== 0) {
setTimeout(() => this.computerCallLandlord(), 1000);
}
}
// 电脑叫地主
computerCallLandlord() {
// 简单AI如果有王或多张大牌叫地主
const hasBigCards = this.players[this.currentPlayer].cards.some(card =>
card.value >= 16 ||
(card.value >= 14 && this.players[this.currentPlayer].cards.filter(c => c.value >= 14).length >= 3)
);
if (hasBigCards) {
this.callLandlord();
} else {
this.noCall();
}
}
// 选择卡片
toggleCardSelection(cardElement) {
if (this.gameStatus !== 'playing' || this.currentPlayer !== 0) return;
cardElement.classList.toggle('selected');
const selected = cardElement.classList.contains('selected');
const suit = cardElement.dataset.suit;
const rank = cardElement.dataset.rank;
const value = parseInt(cardElement.dataset.value);
// 更新卡片对象的选中状态
const card = this.players[0].cards.find(c =>
c.suit === suit && c.rank === rank && c.value === value
);
if (card) {
card.selected = selected;
}
}
// 出牌
playSelectedCards() {
if (this.gameStatus !== 'playing' || this.currentPlayer !== 0) return;
// 获取选中的卡片
const selectedCards = this.players[0].cards.filter(card => card.selected);
if (selectedCards.length === 0) return;
// 验证出牌是否合法
if (this.isValidPlay(selectedCards)) {
// 更新游戏状态
this.players[0].lastPlay = selectedCards;
this.lastPlayPlayer = 0;
this.lastPlayCards = selectedCards;
// 从手中移除出的牌
this.players[0].cards = this.players[0].cards.filter(card => !card.selected);
// 检查是否获胜
if (this.players[0].cards.length === 0) {
this.endGame(0);
return;
}
// 更新UI
this.renderPlayerCards(0);
this.updateCardsCount(0);
this.updateCurrentPlay(0, selectedCards);
this.updateSelfLastPlay(selectedCards);
// 切换到下一个玩家
this.currentPlayer = 1;
this.updateCurrentPlayer();
// 电脑玩家自动出牌
setTimeout(() => this.computerPlay(), 1000);
} else {
alert('出牌不合法,请重新选择');
}
}
// 不出牌
pass() {
if (this.gameStatus !== 'playing' || this.currentPlayer !== 0) return;
// 更新游戏状态
this.players[0].lastPlay = [];
this.updateGameStatus('玩家不出牌');
// 切换到下一个玩家
this.currentPlayer = 1;
this.updateCurrentPlayer();
// 电脑玩家自动出牌
setTimeout(() => this.computerPlay(), 1000);
}
// 电脑出牌
computerPlay() {
const player = this.players[this.currentPlayer];
const playableCards = this.findPlayableCards(player.cards);
if (playableCards.length > 0) {
// 选择最合适的牌组
const selectedCards = this.chooseBestCards(playableCards);
// 更新游戏状态
player.lastPlay = selectedCards;
this.lastPlayPlayer = this.currentPlayer;
this.lastPlayCards = selectedCards;
// 从手中移除出的牌
player.cards = player.cards.filter(card => !selectedCards.includes(card));
// 检查是否获胜
if (player.cards.length === 0) {
this.endGame(this.currentPlayer);
return;
}
// 更新UI
this.renderPlayerCards(this.currentPlayer);
this.updateCardsCount(this.currentPlayer);
this.updateCurrentPlay(this.currentPlayer, selectedCards);
this.updateComputerLastPlay(this.currentPlayer, selectedCards);
this.updateGameStatus(`${player.name} 出牌`);
} else {
// 不出牌
player.lastPlay = [];
this.updateGameStatus(`${player.name} 不出牌`);
}
// 切换到下一个玩家
this.currentPlayer = (this.currentPlayer + 1) % 3;
this.updateCurrentPlayer();
// 如果下一个是电脑,继续自动出牌
if (this.currentPlayer !== 0) {
setTimeout(() => this.computerPlay(), 1000);
}
}
// 查找可出的牌组
findPlayableCards(cards) {
const playable = [];
// 如果是第一个出牌,可以出任何牌组
if (this.lastPlayPlayer === -1) {
// 简单实现:找出所有可能的单牌、对子、顺子等
// 这里简化处理,只考虑单牌
for (const card of cards) {
playable.push([card]);
}
} else {
// 必须出比上一次大的同类型牌
const lastType = this.getCardType(this.lastPlayCards);
const lastMaxValue = Math.max(...this.lastPlayCards.map(c => c.value));
if (lastType === 'single') {
// 找比lastMaxValue大的单牌
const singles = cards.filter(card => card.value > lastMaxValue);
for (const card of singles) {
playable.push([card]);
}
}
}
return playable;
}
// 选择最好的牌组
chooseBestCards(playableCards) {
// 简单AI选择最小的可出牌组
if (playableCards.length === 0) return [];
// 由于只允许单牌,直接选择最小的可出牌
return playableCards[0];
}
// 验证出牌是否合法
isValidPlay(cards) {
if (cards.length === 0) return false;
// 简化版:只允许出单牌
if (cards.length !== 1) {
return false;
}
// 如果是第一个出牌
if (this.lastPlayPlayer === -1) {
return true;
}
// 检查牌型是否相同(都是单牌)
const currentType = this.getCardType(cards);
const lastType = this.getCardType(this.lastPlayCards);
if (currentType !== 'single' || lastType !== 'single') {
return false;
}
// 检查大小
const currentMax = Math.max(...cards.map(c => c.value));
const lastMax = Math.max(...this.lastPlayCards.map(c => c.value));
return currentMax > lastMax;
}
// 获取牌型
getCardType(cards) {
if (cards.length === 0) return 'empty';
if (cards.length === 1) return 'single';
if (cards.length === 2) {
if (cards[0].value === cards[1].value) return 'pair';
if (cards[0].value === 16 && cards[1].value === 17) return 'rocket';
return 'invalid';
}
// 这里可以扩展更多牌型判断
return 'invalid';
}
// 结束游戏
endGame(winner) {
this.gameStatus = 'ended';
const winnerPlayer = this.players[winner];
const isLandlordWin = winnerPlayer.role === '地主';
if (isLandlordWin) {
this.score += 200;
this.updateGameStatus(`地主 ${winnerPlayer.name} 获胜!`);
} else {
this.score -= 100;
this.updateGameStatus(`农民 ${winnerPlayer.name} 获胜!`);
}
// 更新UI
this.elements.landlordButtons.style.display = 'none';
this.elements.playButtons.style.display = 'none';
this.elements.startGame.style.display = 'block';
this.elements.startGame.textContent = '再来一局';
this.updateScore();
}
// 更新游戏状态
updateGameStatus(status) {
this.elements.gameStatus.textContent = status;
}
// 更新当前玩家
updateCurrentPlayer() {
this.elements.currentPlayer.textContent = `当前玩家:${this.players[this.currentPlayer].name}`;
}
// 更新玩家角色
updatePlayerRoles() {
for (let i = 0; i < 3; i++) {
this.elements.playerRoles[i].textContent = this.players[i].role;
}
}
// 渲染所有卡片
renderAllCards() {
for (let i = 0; i < 3; i++) {
this.renderPlayerCards(i);
this.updateCardsCount(i);
}
this.renderLandlordCards();
}
// 渲染玩家卡片
renderPlayerCards(playerIndex) {
const container = this.elements.playerCards[playerIndex];
container.innerHTML = '';
const player = this.players[playerIndex];
if (playerIndex === 0) {
// 自己的卡片,可以选择
for (const card of player.cards) {
const cardElement = card.getHTML(card.selected);
cardElement.addEventListener('click', () => this.toggleCardSelection(cardElement));
container.appendChild(cardElement);
}
} else {
// 电脑玩家的卡片,显示背面
for (let i = 0; i < player.cards.length; i++) {
const cardElement = Card.getBackHTML();
container.appendChild(cardElement);
}
}
}
// 渲染地主牌
renderLandlordCards() {
this.elements.landlordCardsArea.innerHTML = '';
for (const card of this.landlordCards) {
const cardElement = card.getHTML();
this.elements.landlordCardsArea.appendChild(cardElement);
}
}
// 更新牌数
updateCardsCount(playerIndex) {
this.elements.playerCardsCount[playerIndex].textContent = this.players[playerIndex].cards.length;
}
// 更新当前出牌
updateCurrentPlay(playerIndex, cards) {
this.elements.currentPlayInfo.textContent = `${this.players[playerIndex].name} 出牌:`;
this.elements.currentPlayCards.innerHTML = '';
for (const card of cards) {
const cardElement = card.getHTML();
this.elements.currentPlayCards.appendChild(cardElement);
}
}
// 清空当前出牌
clearCurrentPlay() {
this.elements.currentPlayInfo.textContent = '当前出牌:';
this.elements.currentPlayCards.innerHTML = '';
}
// 更新自己的上次出牌
updateSelfLastPlay(cards) {
const container = this.elements.lastPlay[0];
container.innerHTML = '';
for (const card of cards) {
const cardElement = card.getHTML();
container.appendChild(cardElement);
}
}
// 更新电脑玩家的上次出牌
updateComputerLastPlay(playerIndex, cards) {
const container = this.elements.lastPlay[playerIndex];
container.innerHTML = '';
for (const card of cards) {
const cardElement = card.getHTML();
container.appendChild(cardElement);
}
}
// 更新分数
updateScore() {
this.elements.score.textContent = this.score;
}
}
// 初始化游戏
document.addEventListener('DOMContentLoaded', () => {
window.game = new LandlordGame();
});