見出し画像

俺たちのハーツ

Windowsって、「ハーツ」を遊ぶか、「マインスイーパー」を遊ぶか、「ソリティア」を遊ぶか、「フリーセル」を遊ぶか、「ピンボール」を遊ぶための機械だと思っていた。ボードゲーム会でトリテを遊ぶ時、「ハーツみたいなやつだよ」というと、うっすらとわかってもらえた時代があった気がする。(今は、何みたいなものと説明するんだろうか)

遊べないとわかると遊びたくなるのが人情ってものか、ということで、ChatGPTくんに生成してもらうことにする。

※今日も、無駄にコードが多くなってしまったので、アレルギーがある人は、黒い枠のところを全部読み飛ばすとよいと思います。


トライアル1

ハーツで遊びたい!と言ってみると、最初は概形だけ教えてくれる。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hearts Game</title>
  <style>
    /* Add your CSS styles here */
  </style>
</head>
<body>

<div id="game">
  <!-- Add your game elements here -->
</div>

<script>
  // Add your JavaScript code here
</script>

</body>
</html>

さすがに、もうちょっと欲しい、というと、ここまでは作ってくれた。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hearts Game</title>
  <style>
    /* Add your CSS styles here */
     #game {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }

    .card {
      width: 50px;
      height: 75px;
      border: 1px solid #000;
      margin: 5px;
      text-align: center;
      line-height: 75px;
      cursor: pointer;
    }
  </style>
</head>
<body>

<div id="game"></div>

<script>
  document.addEventListener("DOMContentLoaded", function() {
    const suits = ['hearts', 'diamonds', 'clubs', 'spades'];
    const values = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];
    const deck = [];

    // Create a standard 52-card deck
    for (const suit of suits) {
      for (const value of values) {
        deck.push({ suit, value });
      }
    }

    // Shuffle the deck
    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]];
      }
    }
    shuffle(deck);

    // Initialize players
    const players = ['Player', 'Computer 1', 'Computer 2', 'Computer 3'];
    const hands = {};
    for (const player of players) {
      hands[player] = [];
    }

    // Deal cards to players
    for (let i = 0; i < deck.length; i++) {
      hands[players[i % players.length]].push(deck[i]);
    }

    // Render the game
    const gameElement = document.getElementById('game');
    for (const player of players) {
      const playerElement = document.createElement('div');
      playerElement.innerHTML = `<h3>${player}</h3>`;
      for (const card of hands[player]) {
        const cardElement = document.createElement('div');
        cardElement.classList.add('card');
        cardElement.textContent = `${card.value} of ${card.suit}`;
        playerElement.appendChild(cardElement);
      }
      gameElement.appendChild(playerElement);
    }

    // Add your game logic here

  });
</script>

</body>
</html>

ゲームロジックは自分で書くのか。もうちょいなんとかならんのか。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hearts Game</title>
  <style>
    .card {
      width: 50px;
      height: 75px;
      border: 1px solid #000;
      margin: 5px;
      text-align: center;
      line-height: 75px;
      cursor: pointer;
    }
  </style>
</head>
<body>

<div id="game"></div>

<script>
  document.addEventListener("DOMContentLoaded", function() {
    const suits = ['hearts', 'diamonds', 'clubs', 'spades'];
    const values = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];
    const deck = [];

    // Create a standard 52-card deck
    for (const suit of suits) {
      for (const value of values) {
        deck.push({ suit, value });
      }
    }

    // Shuffle the deck
    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]];
      }
    }
    shuffle(deck);

    // Render the game
    const gameElement = document.getElementById('game');
    for (const card of deck) {
      const cardElement = document.createElement('div');
      cardElement.classList.add('card');
      cardElement.textContent = `${card.value} of ${card.suit}`;
      cardElement.addEventListener('click', () => onCardClick(card));
      gameElement.appendChild(cardElement);
    }

    // Handle card click event
    function onCardClick(card) {
      alert(`You clicked the ${card.value} of ${card.suit}!`);
    }
  });
</script>

</body>
</html>

クリックを拾った入力まではやってくれた。
…結論から言うと、これ以上は複雑すぎるといって引き出せなかった。

トライアル2

とはいえ、質問するたびに違う答えを返すことで有名なChatGPTくんなので、別アプローチでもう1回生成してみる。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hearts Game</title>
    <style>
        /* Add some basic styling */
        body {
            font-family: Arial, sans-serif;
            text-align: center;
        }
        #output {
            margin-top: 20px;
        }
    </style>
</head>
<body>

<div id="output"></div>

<script>
    const suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades'];
    const ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Ace'];

    let deck, hands, currentPlayer, trick, leadingSuit, roundNumber, scores;

    function initializeDeck() {
        const deck = [];
        for (const suit of suits) {
            for (const rank of ranks) {
                deck.push({ suit, rank });
            }
        }
        return deck;
    }

    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]];
        }
    }

    function dealCards(deck) {
        const hands = [[], [], []];
        for (let i = 0; i < deck.length; i++) {
            hands[i % 3].push(deck[i]);
        }
        return hands;
    }

    function printHand(player, hand) {
        const output = document.getElementById('output');
        const playerOutput = document.createElement('div');
        playerOutput.innerHTML = `<p>Player ${player + 1}'s hand:</p>`;
        for (const card of hand) {
            playerOutput.innerHTML += `<p>${card.rank} of ${card.suit}</p>`;
        }
        output.appendChild(playerOutput);
    }

    function getPlayerInput() {
        // プレイヤーの入力を受け付けるロジック
        return parseInt(prompt('Select a card to play (1-13):'), 10) - 1;
    }

    function playRandomCard() {
        // ランダムにカードを選ぶロジック
        return Math.floor(Math.random() * 13);
    }

    function playTrick() {
        const currentPlayerHand = hands[currentPlayer];
        const selectedCardIndex = (currentPlayer === 0) ? getPlayerInput() : playRandomCard();
        const selectedCard = currentPlayerHand[selectedCardIndex];

        trick.push(selectedCard);

        if (leadingSuit === null) {
            leadingSuit = selectedCard.suit;
        }

        currentPlayer = (currentPlayer + 1) % 3;
    }

    function determineTrickWinner() {
        const winningIndex = trick.reduce((winner, card, index) => {
            if (card.suit === leadingSuit && (hands[winner].suit !== leadingSuit || ranks.indexOf(card.rank) > ranks.indexOf(hands[winner].rank))) {
                return index;
            }
            return winner;
        }, 0);

        return winningIndex;
    }

    function endRound() {
        const roundPoints = trick.reduce((points, card) => {
            return points + (card.suit === 'Hearts' ? 1 : (card.suit === 'Spades' && card.rank === 'Queen' ? 13 : 0));
        }, 0);

        scores[currentPlayer] += roundPoints;
        roundNumber++;

        trick = [];
        leadingSuit = null;

        if (roundNumber <= 13) {
            initializeRound();
        } else {
            displayScores();
        }
    }

    function initializeRound() {
        // ラウンドの初期化
        hands = dealCards(deck);
        currentPlayer = 0;
    }

    function displayScores() {
        // 得点を表示するロジック
        const output = document.getElementById('output');
        output.innerHTML = '<p>Game Over!</p>';
        for (let player = 0; player < 3; player++) {
            output.innerHTML += `<p>Player ${player + 1}'s score: ${scores[player]}</p>`;
        }
    }

    function playHearts() {
        deck = initializeDeck();
        shuffle(deck);
        scores = [0, 0, 0];
        roundNumber = 1;

        while (roundNumber <= 13) {
            initializeRound();
            displayHands();

            while (trick.length < 3) {
                playTrick();
            }

            const winningIndex = determineTrickWinner();
            currentPlayer = winningIndex;
            endRound();
        }
    }

    function displayHands() {
        const output = document.getElementById('output');
        output.innerHTML = '';

        for (let player = 0; player < 3; player++) {
            printHand(player, hands[player]);
        }
    }

    playHearts();
</script>

</body>
</html>

プレイはランダムだし、入力は数字だが、なんか形にはなってきた。
そして何度かの格闘の後、以下のコードを得た。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hearts Game</title>
    <style>
        /* Add some basic styling */
        body {
            font-family: Arial, sans-serif;
            text-align: center;
        }
        #output {
            margin-top: 20px;
        }
        .card {
            margin: 5px;
            display: inline-block;
        }
        #playCardButton {
            margin-top: 10px;
            padding: 5px 10px;
            font-size: 16px;
            cursor: pointer;
        }
    </style>
</head>
<body>

<div id="output"></div>
<button id="playCardButton" onclick="playUserTurn()">Play Card</button>

<script>
    const suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades'];
    const ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Ace'];

    let deck, hands, currentPlayer, trick, leadingSuit, roundNumber, scores;

    function initializeDeck() {
        const deck = [];
        for (const suit of suits) {
            for (const rank of ranks) {
                deck.push({ suit, rank });
            }
        }
        return deck;
    }

    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]];
        }
    }

    function dealCards(deck) {
        const hands = [[], [], [], []];
        for (let i = 0; i < deck.length; i++) {
            hands[i % 4].push(deck[i]);
        }
        return hands;
    }

    function printHand(player, hand) {
        const output = document.getElementById('output');
        const playerOutput = document.createElement('div');
        playerOutput.innerHTML = `<p>${(player === 0) ? 'Your hand' : `Player ${player}'s hand`}:</p>`;
        
        for (let i = 0; i < hand.length; i++) {
            const card = hand[i];
            playerOutput.innerHTML += `<span class="card">${card.rank} of ${card.suit}</span>`;
        }

        output.appendChild(playerOutput);
    }

    function playUserTurn() {
        if (currentPlayer !== 0) return; // ユーザーのターンでない場合は無視

        const selectedCardIndex = Math.floor(Math.random() * hands[currentPlayer].length);
        const selectedCard = hands[currentPlayer][selectedCardIndex];

        trick.push(selectedCard);

        if (leadingSuit === null) {
            leadingSuit = selectedCard.suit;
        }

        currentPlayer = (currentPlayer + 1) % 4;

        displayHands();

        if (trick.length === 4) {
            const winningIndex = determineTrickWinner();
            currentPlayer = winningIndex;
            endRound();
        } else {
            // コンピュータのターンを開始
            setTimeout(() => {
                playComputerTurn();
            }, 1000);
        }
    }

    function playComputerTurn() {
        if (currentPlayer === 0) return; // ユーザーのターンでない場合は無視

        const currentPlayerHand = hands[currentPlayer];
        const selectedCardIndex = playRandomCard(currentPlayerHand);
        const selectedCard = currentPlayerHand[selectedCardIndex];

        trick.push(selectedCard);

        if (leadingSuit === null) {
            leadingSuit = selectedCard.suit;
        }

        currentPlayer = (currentPlayer + 1) % 4;

        displayHands();

        if (trick.length === 4) {
            const winningIndex = determineTrickWinner();
            currentPlayer = winningIndex;
            endRound();
        }
    }

    function playRandomCard(hand) {
        const validCards = (leadingSuit === null) ? hand : hand.filter(card => card.suit === leadingSuit);
        return Math.floor(Math.random() * validCards.length);
    }

    function determineTrickWinner() {
        const winningIndex = trick.reduce((winner, card, index) => {
            if (card.suit === leadingSuit && (hands[winner].suit !== leadingSuit || ranks.indexOf(card.rank) > ranks.indexOf(hands[winner].rank))) {
                return index;
            }
            return winner;
        }, 0);

        return winningIndex;
    }

    function endRound() {
        const roundPoints = trick.reduce((points, card) => {
            return points + (card.suit === 'Hearts' ? 1 : (card.suit === 'Spades' && card.rank === 'Queen' ? 13 : 0));
        }, 0);

        scores[currentPlayer] += roundPoints;
        roundNumber++;

        trick = [];
        leadingSuit = null;

        if (roundNumber <= 13) {
            initializeRound();
        } else {
            displayScores();
        }
    }

    function initializeRound() {
        hands = dealCards(deck);
        currentPlayer = 0;
        displayHands();

        if (currentPlayer !== 0) {
            // コンピュータのターンを開始
            setTimeout(() => {
                playComputerTurn();
            }, 1000);
        }
    }

    function displayScores() {
        const output = document.getElementById('output');
        output.innerHTML = '<p>Game Over!</p>';
        for (let player = 0; player < 4; player++) {
            output.innerHTML += `<p>${(player === 0) ? 'Your score' : `Player ${player}'s score`}: ${scores[player]}</p>`;
        }
    }

    function displayHands() {
        const output = document.getElementById('output');
        output.innerHTML = `<p>Round: ${roundNumber}, Player ${currentPlayer + 1}'s turn</p>`;

        for (let player = 0; player < 4; player++) {
            printHand(player, (player === 0) ? hands[player] : []);
        }
    }

    function playHearts() {
        deck = initializeDeck();
        shuffle(deck);
        scores = [0, 0, 0, 0];
        roundNumber = 1;

        initializeRound();
    }

    playHearts();
</script>

</body>
</html>

んー、もうヒト押し、って感じだなぁ。

まとめ

ここに、「このコードをHTMLとして保存したら、完全なHeartsが遊べるよ!」とやれればよかったのだが、それをやるためには、トライアル1とトライアル2の内容を、人の手でMixしてやる必要がある。
技術的には、少し時間をもらえれば遊べる形にはできると思うのだが、そこは残念ながら本稿の目指すところではない。(AIに作ってもらいたいじゃないか)

無料版のChatGPT3.5だといまいち形にならないので、本当にハーツを実装してもらうのであれば、よりプログラム実装寄りのAIを選ぶ必要がありそうだ。(Amazonのやつとか、あったよね?)

なんだか今日は肩透かしをくらったような記事になってしまったけれど、Windows7あたりを懐かしむ回ということで、許してほしい。というわけで。今日はこのへんで。ほなね。

例のAI画像

今日は、ちょっと細かい指令で行こうかと思って、「Windowsについていた、ハーツ、ソリティア、マインスイーパー、フリーセルが遊べる地下のゲームセンター。たくさんの筐体が並んでいて、中央の筐体では、かわいいロボットがハーツを遊んでいる」を、スタイル「スチームパンク」で指定してみた。結果、スチームパンク感はあまり出なかったけれど、まぁかわいい画像ができたので、よし。

いただいたサポートは、きっと、ドイツアマゾンからの送料に変わると思います。 温かいご支援、お待ちしております。