ブラックジャック
Web
2025年4月25日

みなさんこんにちは。
ケミストのWeb担当みやのです。
今回は「ブラックジャック」の作成に挑戦してみました。
それではさっそく遊んでみてください。
ディーラー
スコア: 0
プレイヤー
スコア: 0
HTML
HTMLは以下の通りです。
<div class="game-container">
<div class="game-area">
<div id="dealer-area">
<h5>ディーラー</h5>
<div id="dealer-hands" class="hands"></div>
<p id="dealer-score">スコア: 0</p>
</div>
<div id="player-area">
<h5">プレイヤー</h5>
<div id="player-hands" class="hands"></div>
<p id="player-score">スコア: 0</p>
</div>
</div>
<div class="controls">
<button id="hit-btn">HIT</button>
<button id="stand-btn">STAND</button>
<button id="reset-btn">RESET</button>
</div>
<p id="result-message"></p>
</div>
全体が.game-area
その中に#dealer-area、#player-area、.controls、#result-messageという感じで配置します。
CSS
CSSは以下の通りです。
.game-container {
text-align: center;
background-color: #2a2a72;
color: #fff;
margin: 0;
padding-top: 100px;
padding-bottom: 100px;
font-weight: 700;
}
.game-area {
display: flex;
justify-content: space-around;
margin: 20px 0;
}
@media (max-width: 768px) and (orientation: portrait) {
.game-area {
flex-direction: column;
align-items: center;
gap: 20px;
}
}
@media (max-width: 768px) and (orientation: landscape) {
.game-area {
flex-direction: row;
justify-content: space-around;
gap: 10px;
}
}
.hands {
border: 1px solid #fff;
padding: 10px;
min-height: 100px;
display: flex;
gap: 10px;
justify-content: center;
align-items: center;
background-color: #444;
}
.cards {
border: 1px solid #fff;
padding: 10px;
padding-bottom: 15px;
padding-top: 15px;
background-color: #fff;
color: #000;
border-radius: 5px;
font-size: 18px;
}
.cards.red {
color: red;
}
.controls {
margin: 20px 0;
}
#result-message {
font-size: 20px;
margin-top: 20px;
PC時とスマホ横向き時はディーラーとプレイヤーが横並びになるようにしました。
カードの見た目をもうちょっとトランプっぽくしたいですね。
JS
さあ、お前の点数を数えろ!
というわけでスクリプトは以下の通りです。
let deck = [];
let playerHand = [];
let dealerHand = [];
let isGameOver = false;
function initializeDeck() {
const suits = ["♠", "♥", "♦", "♣"];
const values = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"];
deck = [];
for (let suit of suits) {
for (let value of values) {
deck.push({ suit, value });
}
}
deck = deck.sort(() => Math.random() - 0.5);
}
function renderCards(areaId, hand) {
const area = document.getElementById(areaId);
area.innerHTML = "";
hand.forEach(card => {
const cardElement = document.createElement("div");
cardElement.className = "cards";
if (card.suit === "♥" || card.suit === "♦") {
cardElement.classList.add("red");
}
cardElement.textContent = `${card.suit}${card.value}`;
area.appendChild(cardElement);
});
}
function calculateScore(hand) {
let score = 0;
let aceCount = 0;
for (let card of hand) {
if (card.value === "A") {
aceCount++;
score += 11;
} else if (["J", "Q", "K"].includes(card.value)) {
score += 10;
} else {
score += parseInt(card.value);
}
}
while (score > 21 && aceCount > 0) {
score -= 10;
aceCount--;
}
return score;
}
function startGame() {
isGameOver = false;
initializeDeck();
playerHand = [deck.pop(), deck.pop()];
dealerHand = [deck.pop()];
renderCards("player-hands", playerHand);
renderCards("dealer-hands", dealerHand);
updateScores();
document.getElementById("result-message").textContent = "";
}
function updateScores() {
document.getElementById("player-score").textContent = `スコア: ${calculateScore(playerHand)}`;
document.getElementById("dealer-score").textContent = `スコア: ${calculateScore(dealerHand)}`;
}
function endGame() {
const playerScore = calculateScore(playerHand);
let dealerScore = calculateScore(dealerHand);
while (dealerScore < 17) {
dealerHand.push(deck.pop());
dealerScore = calculateScore(dealerHand);
}
renderCards("dealer-hands", dealerHand);
updateScores();
if (playerScore > 21) {
document.getElementById("result-message").textContent = "プレイヤーの負け: バースト";
} else if (dealerScore > 21 || playerScore > dealerScore) {
document.getElementById("result-message").textContent = "プレイヤーの勝ち!";
fireworks();
} else if (playerScore < dealerScore) {
document.getElementById("result-message").textContent = "プレイヤーの負け";
} else {
document.getElementById("result-message").textContent = "引き分け";
}
isGameOver = true;
}
document.getElementById("hit-btn").addEventListener("click", () => {
if (isGameOver) return;
playerHand.push(deck.pop());
renderCards("player-hands", playerHand);
updateScores();
if (calculateScore(playerHand) > 21) {
endGame();
}
});
document.getElementById("stand-btn").addEventListener("click", () => {
if (isGameOver) return;
endGame();
});
document.getElementById("reset-btn").addEventListener("click", startGame);
startGame();
whileとは「条件が満たされるまで繰り返す」という意味だそうです。
今後の課題

・2枚のカードをずらして重ねて表示
・ディーラーにもカードを2枚配って、1枚が表でもう1枚は裏
・トランプ(特に絵札)をリアルにしたい
・カードを配るときにアニメーション
ちょっとずつ改善していきたいです。
ちなみに、CSSだけでブラックジャックを作ってしまった変態天才がいるようです。
絵札とかどうやって表現しているんだろう。