zylyzghc/script.js

601 lines
20 KiB
JavaScript
Raw Normal View History

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