1. 数据准备
- 定义花色和牌面:首先定义了
suits
和ranks
数组来表示扑克牌的花色和牌面值。 - 生成牌组:通过遍历组合生成不含大小王的牌数组
cardsWithoutJokers
,然后添加大小王得到完整牌组cardsWithJokers
。 - 洗牌和发牌:定义了
shuffle
函数用于洗牌,以及dealCards
函数来分配玩家和AI的手牌及底牌。
2. 组件定义 (Dapai
)
- 状态管理:
useState
用于管理玩家、两个AI的手牌、地主身份、是否决定地主、底牌信息、加倍显示状态等。
- 初始化发牌:使用
useEffect
在组件挂载时初始化发牌。 - 事件处理:
handleRobLandlord
和handlePassLandlord
分别处理玩家抢地主和放弃抢地主的逻辑,决定地主身份并控制加倍按钮的显示。aiDoubles
模拟AI的加倍逻辑,当前仅打印决策信息到控制台。handleDouble
和handleNoDouble
处理玩家的加倍或不加倍选择。
- 渲染逻辑:
- 根据当前游戏状态动态渲染玩家、AI的手牌布局,以及抢地主/加倍的UI按钮。
- 当地主确定后,会显示地主的身份,并在中间区域展示地主的底牌(如果地主是玩家,则合并底牌到玩家手中)。
3. UI布局
- 游戏界面分为多个区域,包括玩家手牌区、两个AI手牌区、地主牌显示区,以及操作按钮区。
- 根据不同的游戏阶段(如抢地主阶段、加倍阶段),动态显示相应的UI元素。
4. 交互逻辑
- 玩家可以通过点击按钮参与抢地主、决定是否加倍等关键游戏决策。
- 代码中还包含了简单的AI逻辑,即在无人抢地主时随机决定地主,并模拟AI的加倍决策过程(虽然当前仅输出到控制台)。
const suits = ['♠', '♥', '♦', '♣'];
const ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];
const cardsWithoutJokers = ranks.flatMap(rank => suits.map(suit => `${suit}${rank}`));
const cardsWithJokers = [...cardsWithoutJokers, '大王', '小王'];
// 创建随机洗牌函数
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// 创建发牌函数
function dealCards() {
const allCards = cardsWithJokers;
shuffle(allCards);
const landlordBottom = allCards.slice(51, 54);
const player1 = allCards.slice(0, 17);
const ai1 = allCards.slice(17, 34);
const ai2 = allCards.slice(34, 51);
return [player1, ai1, ai2, landlordBottom];
}
// 游戏组件
const Dapai = () => {
const [playerHand, setPlayerHand] = useState([]);
const [ai1Hand, setAi1Hand] = useState([]);
const [ai2Hand, setAi2Hand] = useState([]);
const [landlord, setLandlord] = useState(null);
const [isLandlordDecided, setIsLandlordDecided] = useState(false);
const [landlordBottom, setLandlordBottom] = useState([]);
const [landlordBottomClassName, setLandlordBottomClassName] = useState('');
const [showDoubleButtons, setShowDoubleButtons] = useState(false);
const [isDoubled, setIsDoubled] = useState(false);
useEffect(() => {
const [player, ai1, ai2, bottom] = dealCards();
setPlayerHand(player);
setAi1Hand(ai1);
setAi2Hand(ai2);
setLandlord(null);
setIsLandlordDecided(false);
setLandlordBottom(bottom);
setLandlordBottomClassName('');
setIsDoubled(false);
}, []);
const handleRobLandlord = () => {
if (!isLandlordDecided) {
setLandlord('player');
setIsLandlordDecided(true);
setLandlordBottomClassName('show');
setShowDoubleButtons(true);
}
};
const handlePassLandlord = () => {
if (!isLandlordDecided) {
const randomAI = Math.random() < 0.5 ? 'ai1' : 'ai2';
setLandlord(randomAI);
setIsLandlordDecided(true);
setLandlordBottomClassName('show');
setShowDoubleButtons(true);
// 当AI成为地主时,AI1和AI2也有机会加倍
if (randomAI !== 'player') {
aiDoubles();
}
}
};
// AI加倍的逻辑
const aiDoubles = () => {
// AI1随机加倍决策
const ai1Decision = Math.random() < 0.5;
console.log(`AI1决定${ai1Decision ? '加倍' : '不加倍'}`);
// AI2随机加倍决策
const ai2Decision = Math.random() < 0.5;
console.log(`AI2决定${ai2Decision ? '加倍' : '不加倍'}`);
};
useEffect(() => {
if (showDoubleButtons && landlord !== null) {
}
if (landlord === 'player') {
// 更新玩家为地主,合并底牌
setPlayerHand([...playerHand, ...landlordBottom]);
} else if (landlord === 'ai1') {
// AI1为地主,合并底牌
setAi1Hand([...ai1Hand, ...landlordBottom]);
} else if (landlord === 'ai2') {
// AI2为地主,合并底牌
setAi2Hand([...ai2Hand, ...landlordBottom]);
}
}, [showDoubleButtons, landlord]);
const handleDouble = () => {
setIsDoubled(true);
setShowDoubleButtons(false);
console.log('玩家加倍');
};
const handleNoDouble = () => {
setIsDoubled(false);
setShowDoubleButtons(false);
console.log('玩家不加倍');
};
return (
<div className="game">
<h1>斗地主</h1>
<div className="hands">
{/* 地主牌在中间,但抢完地主后隐藏 */}
{landlord !== null && (
<div className="hand landlord middle" title="地主牌" style={{ display: landlord === null ? 'none' : 'flex' }}>
{landlordBottom.map((card, index) => (
<div key={card} className="card horizontal-card">
{card}
</div>
))}
</div>
)}
{/* AI2在左上 */}
<div className="hand ai2 top-left" title="AI2">
{ai2Hand.map((card, index) => (
<div key={card} className="card horizontal-card">
{card}
</div>
))}
</div>
{/* AI1在右上 */}
<div className="hand ai1 top-right" title="AI1">
{ai1Hand.map((card, index) => (
<div key={card} className="card horizontal-card">
{card}
</div>
))}
</div>
{/* 玩家牌在底部 */}
<div className="hand player bottom" title="玩家">
{playerHand.map((card, index) => (
<div key={card} className="card horizontal-card">
{card}
</div>
))}
</div>
</div>
{/* 地主决定后的显示逻辑 */}
{landlord !== null ? (
<div className={`hand landlord middle ${landlordBottomClassName}`} title="地主牌">
<p>地主是: {landlord}</p>
</div>
) : (
// 地主未决定时,显示抢地主按钮
<div className="rob-landlord-buttons">
<button onClick={handleRobLandlord}>抢地主</button>
<button onClick={handlePassLandlord}>不抢地主</button>
</div>
)}
{showDoubleButtons && (
<div className="double-buttons">
<button onClick={handleDouble}>加倍</button>
<button onClick={handleNoDouble}>不加倍</button>
</div>
)}
</div>
);
};