ジェネレーティブAIがゲームを作ったり、絵を書いたり、調べ物をしたり #ChatGPT #StableDiffusion #Perplexity

昨年は画像生成AI「Stable Diffusion」で遊びましたが、チャットAI「ChatGPT」や「Perplexity」も面白いですね。とても自然な会話ができるチャットボットで、様々な質問に答えるだけでなく、アドバイス・作業・創作・プログラミングなどなどとても広い用途で使え、Googleが脅威に感じたり、Microsoftが大規模投資したりというのも分かります。

ChatGPTでゲームを作成

ChatGPTはサインアップするだけで基本無料で使えます。(メールアドレスや氏名の登録、SMS認証が必要です)
下のバーに文章を入力すると会話が始まり、回答が行われます。続けて入力すると、前の会話を踏まえた回答がされます。日本語も使えます。

とても人気なので、サーバ負荷が高いときは使えない場合もあります。月額20ドルで優先的に使える「ChatGPT Plus」サービスも米国で始まっており、近いうちに他国でも展開されるそうです。

ブロック崩しのソースコードを作ってもらいました。できたコードが下記です。そのまま動きました。(ソースコードが途中で止まりましたが続きを出力させると全部出力されました)
ソースの説明や改良アイデア、デバッグなんかも可能ですね。

<!DOCTYPE html>
<html>
<head>
  <style>
    canvas {
      border: 1px solid black;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas" width="500" height="500"></canvas>
  <script>
    const canvas = document.getElementById("myCanvas");
    const ctx = canvas.getContext("2d");

    let x = canvas.width / 2;
    let y = canvas.height - 30;
    let dx = 2;
    let dy = -2;
    let ballRadius = 10;
    let paddleHeight = 10;
    let paddleWidth = 75;
    let paddleX = (canvas.width - paddleWidth) / 2;
    let rightPressed = false;
    let leftPressed = false;
    let brickRowCount = 5;
    let brickColumnCount = 3;
    let brickWidth = 75;
    let brickHeight = 20;
    let brickPadding = 10;
    let brickOffsetTop = 30;
    let brickOffsetLeft = 30;
    let score = 0;

    const bricks = [];
    for (let c = 0; c < brickColumnCount; c++) {
      bricks[c] = [];
      for (let r = 0; r < brickRowCount; r++) {
        bricks[c][r] = { x: 0, y: 0, status: 1 };
      }
    }

    document.addEventListener("keydown", keyDownHandler, false);
    document.addEventListener("keyup", keyUpHandler, false);
    document.addEventListener("mousemove", mouseMoveHandler, false);

    function keyDownHandler(e) {
      if (e.key === "Right" || e.key === "ArrowRight") {
        rightPressed = true;
      } else if (e.key === "Left" || e.key === "ArrowLeft") {
        leftPressed = true;
      }
    }

    function keyUpHandler(e) {
      if (e.key === "Right" || e.key === "ArrowRight") {
        rightPressed = false;
      } else if (e.key === "Left" || e.key === "ArrowLeft") {
        leftPressed = false;
      }
    }

    function mouseMoveHandler(e) {
      let relativeX = e.clientX - canvas.offsetLeft;
      if (relativeX > 0 && relativeX < canvas.width) {
        paddleX = relativeX - paddleWidth / 2;
      }
    }

    function collisionDetection() {
      for (let c = 0; c < brickColumnCount; c++) {
        for (let r = 0; r < brickRowCount; r++) {
          let b = bricks[c][r];
          if (b.status === 1) {
            if (
              x > b.x &&
              x < b.x + brickWidth &&
              y > b.y &&
              y < b.y + brickHeight
            ) {
              dy = -dy;
              b.status = 0;
              score++;
              if (score === brickRowCount * brickColumnCount) {
                alert("You win! Congratulations!");
                document.location.reload();
              }
            }
          }
        }
      }
    }
                
    function drawBall() {
      ctx.beginPath();
      ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
      ctx.fillStyle = "#0095DD";
      ctx.fill();
      ctx.closePath();
    }
                
    function drawPaddle() {
      ctx.beginPath();
      ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
      ctx.fillStyle = "#0095DD";
      ctx.fill();
      ctx.closePath();
    }
                
    function drawBricks() {
      for (let c = 0; c < brickColumnCount; c++) {
        for (let r = 0; r < brickRowCount; r++) {
          if (bricks[c][r].status === 1) {
            let brickX = r * (brickWidth + brickPadding) + brickOffsetLeft;
            let brickY = c * (brickHeight + brickPadding) + brickOffsetTop;
            bricks[c][r].x = brickX;
            bricks[c][r].y = brickY;
            ctx.beginPath();
            ctx.rect(brickX, brickY, brickWidth, brickHeight);
            ctx.fillStyle = "#0095DD";
            ctx.fill();
            ctx.closePath();
          }
        }
      }
    }
                
    function drawScore() {
      ctx.font = "16px Arial";
      ctx.fillStyle = "#0095DD";
      ctx.fillText("Score: " + score, 8, 20);
    }
                
    function draw() {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      drawBricks();
      drawBall();
      drawPaddle();
      drawScore();
      collisionDetection();
                
      if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
        dx = -dx;
      }
      if (y + dy < ballRadius) {
        dy = -dy;
      } else if (y + dy > canvas.height - ballRadius) {
        if (x > paddleX && x < paddleX + paddleWidth) {
          dy = -dy;
        } else {
          alert("Game over");
          document.location.reload();
        }
      }
                
      if (rightPressed && paddleX < canvas.width - paddleWidth) {
        paddleX += 7;
      } else if (leftPressed && paddleX > 0) {
        paddleX -= 7;
      }
                
      x += dx;
      y += dy;
                
      requestAnimationFrame(draw);
    }
                
    draw();
  </script>
</body>
</html>

ChatGPTとStable Diffusionでコンセプトアートを描く

次はコンセプトアートを考えてもらいました。この文章をStable Diffusionで絵にしてもらうことで、細かくプロンプトを考えなくても意味を持った絵が生成できたりしますね。

ChatGPTの調べ物は注意が必要

ChatGPTはそれっぽい回答をしますが、必ずしも正解を返すわけではありません。物知りの知ったかぶりのような回答が多いので注意です。逆にそのおかげで、それっぽい創作なんかも可能です。

ChatGPTが考えた銀河鉄道の夜のコンセプトアートの1パターンをStable Diffusionで描いてみました。

Perplexityでの調べ物の方が参考URL付きで真偽が分かりやすい

ChatGPTと似たようなチャットAIとして「Perplexity」というのがあります。こちらは今のところユーザ登録等必要なく無償で利用できます。

日本語で質問が可能ですが、回答は英語になるみたいです(記事執筆時点)。
ChatGPTと比べて特長的なのは、回答の根拠となる参考URLも合わせて提示してくれるので、回答が本当かどうか分かりやすい点です。
調べ物なんかはこちらの方が便利ですね。先程のChatGPTへの質問と同じ質問をしても、こちらは正しい回答をしてくれました。

View Detailedを押すとさらに詳しく回答してくれます。

saya: