// 卡片类 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(); });