赤背景は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);
}