赤背景はAI生成部分
青背景はAI補助部分
ファイル構成
├── index.html
├── style.css
└── script.js
index.html
<!DOCTYPE html>
<html>
<head>
<title>Tic Tac Toe</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="app">
<div class="turn-container">
<span class="turn-label">Turn</span>
<div id="currentTurn"></div>
</div>
<div class="board-container">
<div class="board">
<div class="row">
<div class="cell" id="0"></div>
<div class="cell" id="1"></div>
<div class="cell" id="2"></div>
</div>
<div class="row">
<div class="cell" id="3"></div>
<div class="cell" id="4"></div>
<div class="cell" id="5"></div>
</div>
<div class="row">
<div class="cell" id="6"></div>
<div class="cell" id="7"></div>
<div class="cell" id="8"></div>
</div>
</div>
<div class="win" id="winArea" style="display: none;">
<div class="win-box">
<span id="winLabel">WINNER</span>
<div id="winPlayer"></div>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
script.js
const cells = [];
const data = [0,0,0,0,0,0,0,0,0];
let currentPlayer = 0;
const winPlayer = document.getElementById('winPlayer');
const winLabel = document.getElementById('winLabel');
const turnUI = document.getElementById('currentTurn');
const winArea = document.getElementById('winArea');
for (let i = 0; i < 9; i++) {
const cell = document.getElementById(String(i));
cell.onclick = () => {set(i)};
cells.push(cell);
}
function set(i){
if (data[i] != 0) return;
data[i] = currentPlayer + 1;
if (currentPlayer == 0){
drawCircle(i);
} else {
drawCross(i);
}
currentPlayer = (currentPlayer + 1)%2;
check();
updateTurnUI();
}
function drawCircle(i){
cells[i].classList.add('circle');
}
function drawCross(i){
cells[i].classList.add('cross');
}
function check() {
const winPatterns = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], // 横
[0, 3, 6], [1, 4, 7], [2, 5, 8], // 縦
[0, 4, 8], [2, 4, 6] // 斜め
];
for (const pattern of winPatterns) {
const [a, b, c] = pattern;
if (data[a]!==0 && data[a]===data[b] && data[b] === data[c]) {
if (data[a]==1){
winPlayer.classList.add('circle');
} else {
winPlayer.classList.add('cross');
}
endGame();
return;
}
}
// 引き分け判定
if (!data.includes(0)) {
winLabel.textContent = 'DRAW'; // ラベルをDRAWにする
winPlayer.style.display = 'none'; // マルバツ用のボックスは隠す
endGame();
}
}
function updateTurnUI(){
if (currentPlayer == 0) {
turnUI.classList.remove('cross');
turnUI.classList.add('circle');
} else {
turnUI.classList.remove('circle');
turnUI.classList.add('cross');
}
}
function endGame() {
cells.forEach(cell => cell.onclick = null);
winArea.style.display = "flex"; // 盤面の真上にabsoluteで重なる
}
// 初期ターン表示
updateTurnUI();
style.css
body {
font-family: sans-serif;
margin: 0;
padding: 0;
}
.app {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
max-height: 100vh;
aspect-ratio: 4 / 3;
background-color: #000;
}
.turn-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100px;
height: 100px;
margin-bottom: 20px;
background: #111;
border-radius: 10%;
border: 2px solid #da00ff;
box-shadow:
0 0 5px #d0f,
0 0 15px #b0f,
inset 0 0 10px #80f;
}
.turn-label {
color: #b0f;
font-size: 14px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 5px;
text-shadow: 0 0 5px #b0f;
}
#currentTurn {
width: 40px;
height: 40px;
}
.board-container {
position: relative;
width: 45%;
max-width: 400px;
aspect-ratio: 1 / 1;
}
.board {
width: 100%;
height: 100%;
background: #80f;
border-radius: 10%;
border: 2px solid #fff;
box-shadow:
0 0 5px #d0f,
0 0 15px #b0f,
0 0 30px #80f,
0 0 50px #00f;
overflow: hidden;
display: flex;
flex-direction: column;
gap: 2px;
transform: translateZ(0);
will-change: transform;
}
.row {
flex: 1;
display: flex;
flex-direction: row;
gap: 2px;
}
.cell {
flex: 1;
background-color: #111;
box-shadow: inset 0 0 10px #80f;
}
.cell, #currentTurn, #winPlayer {
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.win {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(5, 5, 5, 0.92);
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
animation: fadeIn 0.3s ease-out;
border-radius: calc(10% - 2px);
}
.win-box {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
#winLabel {
color: #fff;
font-size: 28px;
font-weight: bold;
letter-spacing: 2px;
text-shadow: 0 0 10px #fff;
}
#winPlayer {
width: 80px;
height: 80px;
}
@keyframes fadeIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
.circle::before {
content: "";
width: 60%;
height: 60%;
border-radius: 50%;
border: 4px solid #f00;
box-sizing: border-box;
box-shadow:
0 0 8px #f00,
0 0 20px #f44,
inset 0 0 8px #f44;
}
.cross::before,
.cross::after {
content: "";
position: absolute;
top: 50%;
left: 20%;
width: 60%;
height: 4px;
background-color: #4f4;
border-radius: 2px;
box-shadow:
0 0 8px #4f4,
0 0 20px #8f8;
}
.cross::before {
transform: translateY(-50%) rotate(45deg);
}
.cross::after {
transform: translateY(-50%) rotate(-45deg);
}