国标麻将一抽胡

我将创建一个简单的国标麻将一抽胡游戏,玩家每次随机获得一个听牌牌型,然后从一组牌中抽取一张,看是否能胡牌。

思路分析

  1. 随机生成各种国标麻将听牌牌型(缺一张即可胡牌)

  2. 显示当前牌型,其中一张牌为"?"表示待抽牌

  3. 准备一组待抽牌,其中包含可以胡牌的牌和其他无关牌

  4. 玩家点击抽牌,显示抽牌结果和是否胡牌

下面是完整代码实现:

<!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>

* {

box-sizing: border-box;

margin: 0;

padding: 0;

font-family: 'Microsoft YaHei', sans-serif;

}

body {

background: linear-gradient(135deg, #1a5f23 0%, #0d3b1a 100%);

color: #fff;

min-height: 100vh;

padding: 20px;

display: flex;

flex-direction: column;

align-items: center;

}

.container {

max-width: 900px;

width: 100%;

background-color: rgba(0, 0, 0, 0.6);

border-radius: 20px;

padding: 30px;

box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);

margin-top: 20px;

}

header {

text-align: center;

margin-bottom: 30px;

width: 100%;

}

h1 {

font-size: 2.8rem;

color: #ffde59;

text-shadow: 3px 3px 0 #d35400;

margin-bottom: 10px;

letter-spacing: 2px;

}

.subtitle {

font-size: 1.2rem;

color: #b8e994;

margin-bottom: 20px;

}

.game-area {

display: flex;

flex-wrap: wrap;

justify-content: space-between;

gap: 30px;

}

.hand-section, .draw-section {

flex: 1;

min-width: 300px;

background-color: rgba(10, 60, 20, 0.7);

border-radius: 15px;

padding: 25px;

box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);

}

.section-title {

font-size: 1.8rem;

color: #ffde59;

margin-bottom: 20px;

text-align: center;

border-bottom: 2px solid #ffde59;

padding-bottom: 10px;

}

.current-hand {

display: flex;

flex-wrap: wrap;

gap: 12px;

justify-content: center;

margin-bottom: 25px;

min-height: 120px;

}

.tile {

width: 70px;

height: 100px;

background-color: #fff3cd;

border-radius: 8px;

display: flex;

flex-direction: column;

justify-content: center;

align-items: center;

font-size: 1.8rem;

font-weight: bold;

color: #222;

box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);

transition: all 0.3s;

cursor: default;

position: relative;

overflow: hidden;

}

.tile::before {

content: '';

position: absolute;

top: 5px;

left: 5px;

right: 5px;

bottom: 5px;

border: 1px solid rgba(0, 0, 0, 0.1);

border-radius: 5px;

}

.tile.character {

color: #c0392b;

}

.tile.bamboo {

color: #27ae60;

}

.tile.dot {

color: #2980b9;

}

.tile.wind {

color: #8e44ad;

}

.tile.dragon {

color: #d35400;

}

.tile.missing {

background-color: #ffcccc;

color: #c0392b;

font-size: 2.5rem;

}

.tile-value {

font-size: 1.4rem;

}

.tile-type {

font-size: 1rem;

margin-top: 5px;

}

.hand-info {

background-color: rgba(0, 0, 0, 0.3);

padding: 15px;

border-radius: 10px;

text-align: center;

margin-top: 20px;

}

.hand-info h3 {

color: #ffde59;

margin-bottom: 10px;

}

.draw-options {

display: flex;

flex-wrap: wrap;

gap: 15px;

justify-content: center;

margin-bottom: 30px;

}

.draw-tile {

width: 80px;

height: 110px;

background-color: #fff3cd;

border-radius: 8px;

display: flex;

flex-direction: column;

justify-content: center;

align-items: center;

font-size: 1.8rem;

font-weight: bold;

color: #222;

box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);

transition: all 0.3s;

cursor: pointer;

position: relative;

}

.draw-tile:hover {

transform: translateY(-10px);

box-shadow: 0 15px 20px rgba(0, 0, 0, 0.4);

}

.draw-tile:active {

transform: translateY(-5px);

}

.draw-tile.selected {

transform: translateY(-10px);

box-shadow: 0 0 20px #ffde59;

border: 3px solid #ffde59;

}

.draw-button {

display: block;

margin: 30px auto;

padding: 15px 40px;

font-size: 1.5rem;

background: linear-gradient(to bottom, #ffde59, #f39c12);

color: #8b4513;

border: none;

border-radius: 50px;

cursor: pointer;

font-weight: bold;

box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);

transition: all 0.3s;

}

.draw-button:hover {

transform: scale(1.05);

box-shadow: 0 15px 25px rgba(0, 0, 0, 0.4);

}

.draw-button:active {

transform: scale(0.98);

}

.draw-button:disabled {

background: #cccccc;

color: #666666;

cursor: not-allowed;

transform: none;

box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);

}

.result-section {

margin-top: 40px;

background-color: rgba(0, 0, 0, 0.5);

border-radius: 15px;

padding: 25px;

text-align: center;

display: none;

}

.result-section.show {

display: block;

animation: fadeIn 0.8s;

}

@keyframes fadeIn {

from { opacity: 0; }

to { opacity: 1; }

}

.result-title {

font-size: 2.2rem;

margin-bottom: 15px;

}

.result-win {

color: #2ecc71;

}

.result-lose {

color: #e74c3c;

}

.result-details {

font-size: 1.3rem;

line-height: 1.8;

margin-bottom: 20px;

}

.winning-tile {

display: inline-block;

padding: 5px 15px;

background-color: #fff3cd;

color: #c0392b;

border-radius: 5px;

font-weight: bold;

font-size: 1.4rem;

margin: 0 5px;

}

.stats {

display: flex;

justify-content: space-around;

margin-top: 30px;

background-color: rgba(0, 0, 0, 0.3);

padding: 20px;

border-radius: 15px;

}

.stat-item {

text-align: center;

}

.stat-value {

font-size: 2.5rem;

font-weight: bold;

color: #ffde59;

}

.stat-label {

font-size: 1.1rem;

color: #b8e994;

}

.rules {

margin-top: 30px;

background-color: rgba(10, 60, 20, 0.7);

padding: 20px;

border-radius: 15px;

font-size: 1.1rem;

line-height: 1.6;

}

.rules h3 {

color: #ffde59;

margin-bottom: 10px;

}

.rules ul {

padding-left: 20px;

}

.rules li {

margin-bottom: 8px;

}

footer {

margin-top: 30px;

text-align: center;

color: #b8e994;

font-size: 0.9rem;

width: 100%;

padding-top: 20px;

border-top: 1px solid rgba(255, 255, 255, 0.1);

}

@media (max-width: 768px) {

.game-area {

flex-direction: column;

}

.hand-section, .draw-section {

width: 100%;

}

h1 {

font-size: 2rem;

}

.tile {

width: 55px;

height: 80px;

font-size: 1.5rem;

}

.draw-tile {

width: 65px;

height: 90px;

font-size: 1.5rem;

}

}

</style>

</head>

<body>

<header>

<h1>国标麻将一抽胡</h1>

<p class="subtitle">随机生成听牌牌型,只需一抽即可胡牌!</p>

</header>

<div class="container">

<div class="game-area">

<div class="hand-section">

<h2 class="section-title">当前牌型</h2>

<div class="current-hand" id="currentHand">

<!-- 牌型将通过JS动态生成 -->

</div>

<div class="hand-info">

<h3>听牌状态</h3>

<p id="handDescription">您当前有一个听牌牌型,只需要抽取正确的牌即可胡牌!</p>

</div>

</div>

<div class="draw-section">

<h2 class="section-title">抽取一张牌</h2>

<div class="draw-options" id="drawOptions">

<!-- 抽牌选项将通过JS动态生成 -->

</div>

<button class="draw-button" id="drawButton">抽取选中的牌</button>

</div>

</div>

<div class="result-section" id="resultSection">

<!-- 结果将通过JS动态生成 -->

</div>

<div class="stats">

<div class="stat-item">

<div class="stat-value" id="gamesPlayed">0</div>

<div class="stat-label">游戏次数</div>

</div>

<div class="stat-item">

<div class="stat-value" id="wins">0</div>

<div class="stat-label">胡牌次数</div>

</div>

<div class="stat-item">

<div class="stat-value" id="winRate">0%</div>

<div class="stat-label">胡牌率</div>

</div>

</div>

<div class="rules">

<h3>游戏规则</h3>

<ul>

<li>系统随机生成一个国标麻将听牌牌型(缺一张即可胡牌)</li>

<li>牌型中的"?"表示缺的那张牌</li>

<li>从右侧5张牌中选择一张抽取</li>

<li>如果抽到正确的牌,则胡牌!否则不胡</li>

<li>每次游戏都会随机生成新的牌型和待抽牌</li>

<li>国标麻将胡牌牌型包括:平胡、碰碰胡、混一色等</li>

</ul>

</div>

</div>

<footer>

<p>国标麻将一抽胡游戏 | 仅用于娱乐目的 | 使用HTML5、CSS3和JavaScript开发</p>

</footer>

<script>

// 麻将牌数据

const mahjongTiles = {

characters: ['1万', '2万', '3万', '4万', '5万', '6万', '7万', '8万', '9万'],

bamboos: ['1条', '2条', '3条', '4条', '5条', '6条', '7条', '8条', '9条'],

dots: ['1筒', '2筒', '3筒', '4筒', '5筒', '6筒', '7筒', '8筒', '9筒'],

winds: ['东风', '南风', '西风', '北风'],

dragons: ['红中', '发财', '白板']

};

// 所有牌的数组

const allTiles = [

...mahjongTiles.characters,

...mahjongTiles.bamboos,

...mahjongTiles.dots,

...mahjongTiles.winds,

...mahjongTiles.dragons

];

// 预定义的听牌牌型(13张牌+1张听的牌)

const winningHands = [

{

name: "平胡",

tiles: ["1万", "2万", "3万", "4筒", "5筒", "6筒", "7条", "8条", "9条", "东风", "东风", "东风", "5万", "7万"],

winningTile: "6万",

description: "平胡:基本胡牌牌型,由四组顺子或刻子加一对将组成"

},

{

name: "碰碰胡",

tiles: ["1万", "1万", "1万", "3条", "3条", "3条", "5筒", "5筒", "5筒", "北风", "北风", "北风", "红中"],

winningTile: "红中",

description: "碰碰胡:由四组刻子(三张相同)加一对将组成"

},

{

name: "混一色",

tiles: ["1万", "2万", "3万", "4万", "5万", "6万", "7万", "8万", "9万", "东风", "东风", "东风", "5万"],

winningTile: "5万",

description: "混一色:由一种花色牌加上字牌组成的胡牌牌型"

},

{

name: "清一色",

tiles: ["1条", "2条", "3条", "4条", "4条", "4条", "5条", "6条", "7条", "8条", "8条", "8条", "9条"],

winningTile: "9条",

description: "清一色:全部由同一种花色的牌组成的胡牌牌型"

},

{

name: "全带幺",

tiles: ["1万", "1万", "1万", "9万", "9万", "9万", "1筒", "2筒", "3筒", "7筒", "8筒", "9筒", "白板"],

winningTile: "白板",

description: "全带幺:所有顺子、刻子、将牌都包含1或9"

},

{

name: "七对",

tiles: ["1万", "1万", "3万", "3万", "5万", "5万", "7万", "7万", "9条", "9条", "东风", "东风", "发财"],

winningTile: "发财",

description: "七对:由七个对子组成的特殊胡牌牌型"

},

{

name: "十三幺",

tiles: ["1万", "9万", "1条", "9条", "1筒", "9筒", "东风", "南风", "西风", "北风", "红中", "发财", "白板"],

winningTile: "白板",

description: "十三幺:由13种幺九牌各一张,再加上其中任意一张组成"

}

];

// 游戏状态

let gameState = {

currentHand: null,

winningTile: null,

drawOptions: [],

selectedTileIndex: null,

gamesPlayed: 0,

wins: 0

};

// DOM元素

const currentHandEl = document.getElementById('currentHand');

const drawOptionsEl = document.getElementById('drawOptions');

const drawButton = document.getElementById('drawButton');

const resultSection = document.getElementById('resultSection');

const handDescription = document.getElementById('handDescription');

const gamesPlayedEl = document.getElementById('gamesPlayed');

const winsEl = document.getElementById('wins');

const winRateEl = document.getElementById('winRate');

// 初始化游戏

function initGame() {

// 随机选择一个听牌牌型

const handIndex = Math.floor(Math.random() * winningHands.length);

const selectedHand = winningHands[handIndex];

// 复制牌型数组

const handTiles = [...selectedHand.tiles];

gameState.winningTile = selectedHand.winningTile;

// 随机选择一张牌作为缺失的牌(用"?"表示)

const missingTileIndex = Math.floor(Math.random() * handTiles.length);

gameState.currentHand = {

tiles: handTiles,

missingIndex: missingTileIndex,

name: selectedHand.name,

description: selectedHand.description

};

// 更新描述

handDescription.textContent = selectedHand.description;

// 生成待抽牌选项(包含正确的牌和4张随机牌)

gameState.drawOptions = generateDrawOptions(gameState.winningTile);

gameState.selectedTileIndex = null;

// 更新UI

renderCurrentHand();

renderDrawOptions();

// 隐藏结果

resultSection.classList.remove('show');

// 启用抽牌按钮

drawButton.disabled = true;

drawButton.textContent = "抽取选中的牌";

}

// 生成待抽牌选项

function generateDrawOptions(winningTile) {

const options = [winningTile];

// 添加4张随机牌(不能与正确牌重复)

while (options.length < 5) {

const randomTile = allTiles[Math.floor(Math.random() * allTiles.length)];

if (!options.includes(randomTile) && randomTile !== winningTile) {

options.push(randomTile);

}

}

// 打乱数组

return shuffleArray(options);

}

// 打乱数组(Fisher-Yates洗牌算法)

function shuffleArray(array) {

const newArray = [...array];

for (let i = newArray.length - 1; i > 0; i--) {

const j = Math.floor(Math.random() * (i + 1));

newArray\[i\], newArray\[j\]\] = \[newArray\[j\], newArray\[i\]\]; } return newArray; } // 渲染当前手牌 function renderCurrentHand() { currentHandEl.innerHTML = ''; const hand = gameState.currentHand; hand.tiles.forEach((tile, index) =\> { const tileEl = document.createElement('div'); tileEl.className = 'tile'; if (index === hand.missingIndex) { tileEl.classList.add('missing'); tileEl.innerHTML = '?'; } else { // 根据牌的类型添加对应的CSS类 if (tile.includes('万')) tileEl.classList.add('character'); else if (tile.includes('条')) tileEl.classList.add('bamboo'); else if (tile.includes('筒')) tileEl.classList.add('dot'); else if (tile.includes('风')) tileEl.classList.add('wind'); else tileEl.classList.add('dragon'); const tileValue = tile.replace(/\[\^0-9\]/g, '') \|\| tile.substring(0, 1); const tileType = tile.replace(/\[0-9\]/g, '') \|\| tile.substring(1); tileEl.innerHTML = \` \

${tileValue}\ \
${tileType}\ \`; } currentHandEl.appendChild(tileEl); }); } // 渲染抽牌选项 function renderDrawOptions() { drawOptionsEl.innerHTML = ''; gameState.drawOptions.forEach((tile, index) =\> { const tileEl = document.createElement('div'); tileEl.className = 'draw-tile'; // 根据牌的类型添加对应的CSS类 if (tile.includes('万')) tileEl.classList.add('character'); else if (tile.includes('条')) tileEl.classList.add('bamboo'); else if (tile.includes('筒')) tileEl.classList.add('dot'); else if (tile.includes('风')) tileEl.classList.add('wind'); else tileEl.classList.add('dragon'); const tileValue = tile.replace(/\[\^0-9\]/g, '') \|\| tile.substring(0, 1); const tileType = tile.replace(/\[0-9\]/g, '') \|\| tile.substring(1); tileEl.innerHTML = \` \
${tileValue}\ \
${tileType}\ \`; tileEl.addEventListener('click', () =\> selectDrawTile(index)); drawOptionsEl.appendChild(tileEl); }); } // 选择抽牌 function selectDrawTile(index) { // 移除之前选中的样式 document.querySelectorAll('.draw-tile').forEach(tile =\> { tile.classList.remove('selected'); }); // 添加当前选中的样式 document.querySelectorAll('.draw-tile')\[index\].classList.add('selected'); // 更新选中的索引 gameState.selectedTileIndex = index; // 启用抽牌按钮 drawButton.disabled = false; } // 抽取选中的牌 function drawSelectedTile() { if (gameState.selectedTileIndex === null) return; const selectedTile = gameState.drawOptions\[gameState.selectedTileIndex\]; const isWin = selectedTile === gameState.winningTile; // 更新游戏统计 gameState.gamesPlayed++; if (isWin) gameState.wins++; // 更新统计显示 updateStats(); // 显示结果 showResult(isWin, selectedTile); // 禁用抽牌按钮 drawButton.disabled = true; drawButton.textContent = "重新开始游戏"; // 更改按钮功能为重新开始 drawButton.onclick = restartGame; } // 显示结果 function showResult(isWin, selectedTile) { resultSection.innerHTML = ''; const resultTitle = document.createElement('h2'); resultTitle.className = \`result-title ${isWin ? 'result-win' : 'result-lose'}\`; resultTitle.textContent = isWin ? '恭喜胡牌!' : '很遗憾,没有胡牌'; const resultDetails = document.createElement('div'); resultDetails.className = 'result-details'; const winningTileSpan = document.createElement('span'); winningTileSpan.className = 'winning-tile'; winningTileSpan.textContent = gameState.winningTile; const selectedTileSpan = document.createElement('span'); selectedTileSpan.className = 'winning-tile'; selectedTileSpan.textContent = selectedTile; if (isWin) { resultDetails.innerHTML = \` 您抽中了\${selectedTile}\,成功胡牌! \胡牌牌型:\${gameState.currentHand.name}\ \${gameState.currentHand.description} \`; } else { resultDetails.innerHTML = \` 您抽中了\${selectedTile}\,但这不是胡牌所需要的牌。 \胡牌需要的牌是:\${gameState.winningTile}\ \胡牌牌型:\${gameState.currentHand.name}\ \`; } const restartButton = document.createElement('button'); restartButton.className = 'draw-button'; restartButton.textContent = '再来一局'; restartButton.onclick = restartGame; resultSection.appendChild(resultTitle); resultSection.appendChild(resultDetails); resultSection.appendChild(restartButton); resultSection.classList.add('show'); } // 重新开始游戏 function restartGame() { initGame(); drawButton.onclick = drawSelectedTile; } // 更新统计 function updateStats() { gamesPlayedEl.textContent = gameState.gamesPlayed; winsEl.textContent = gameState.wins; const winRate = gameState.gamesPlayed \> 0 ? Math.round((gameState.wins / gameState.gamesPlayed) \* 100) : 0; winRateEl.textContent = \`${winRate}%\`; } // 初始化事件监听器 drawButton.addEventListener('click', drawSelectedTile); // 初始化游戏 initGame(); // 添加键盘事件支持 document.addEventListener('keydown', (e) =\> { if (e.key \>= '1' \&\& e.key \<= '5') { const index = parseInt(e.key) - 1; if (index \< gameState.drawOptions.length) { selectDrawTile(index); } } else if (e.key === 'Enter' \|\| e.key === ' ') { if (!drawButton.disabled) { drawSelectedTile(); } else if (resultSection.classList.contains('show')) { restartGame(); } } else if (e.key === 'r' \|\| e.key === 'R') { restartGame(); } }); \ \ \ 游戏功能说明 1. 牌型生成:游戏随机生成7种不同的国标麻将听牌牌型,包括平胡、碰碰胡、混一色、清一色、全带幺、七对和十三幺。 2. 游戏流程: · 系统显示一个听牌牌型,其中一张牌用"?"表示缺失 · 右侧显示5张待抽牌,其中一张是正确的胡牌 · 玩家选择一张牌抽取 · 显示结果,告诉玩家是否胡牌 3. 游戏特性: · 每次游戏都会随机生成新的牌型和待抽牌 · 记录游戏统计:游戏次数、胡牌次数和胡牌率 · 支持键盘操作(按1-5选择牌,按Enter或空格键抽取) · 响应式设计,适配各种屏幕尺寸 4. 视觉效果: · 采用麻将主题的绿色和金色配色 · 牌面设计区分不同的花色(万、条、筒、风牌、箭牌) · 动画效果提升游戏体验 您可以直接复制上面的代码到HTML文件中,然后在浏览器中打开即可开始游戏!

相关推荐
m0_502724952 小时前
CSS实现容器的宽度由内容决定
前端·css
dear_bi_MyOnly2 小时前
用 Vibe Coding 打造 React 飞机大战游戏 —— 我的实践与学习心得
前端·react.js·游戏
TheNextByte12 小时前
如何将 Safari 标签转移到新 iPhone 17?
前端·iphone·safari
m0_748245922 小时前
常见状态前缀
前端·css
用户90443816324602 小时前
拒绝 `setInterval`!手撕“死了么”生命倒计时,带你看看 60FPS 下的 Web Worker 优雅多线程
前端·javascript
ttod_qzstudio2 小时前
CSS 样式优先级原则详解:从一个 Vue 组件样式冲突案例说起
前端·css·vue.js
5967851542 小时前
css装饰
前端·css·css3
石像鬼₧魂石2 小时前
补充章节:WPScan 实战后的 “打扫战场 + 溯源” 流程
数据库·学习·mysql
MarkHD7 小时前
智能体在车联网中的应用:第51天 模仿学习与离线强化学习:破解数据效率与安全困局的双刃剑
学习·安全