GPT-4oの一歩先へ!AIでテトリスを作ってみたら、まさかの大興奮
突然ですが、AIでテトリスを作ってみたいと思ったことはありませんか?そんな夢が、なんと現実に!しかも、驚くほど簡単にできてしまったんです。
どうやって作ったの?
手順はとてもシンプル。まずは、AIに「ブラウザで動くテトリスを作ってください」と指示を出します。すると、AIが瞬く間にHTML、CSS、そしてJavaScriptを作成してくれます。驚くべきことに、このコードをそのまま使うだけで、テトリスが動くのです。
AIに指示を出す
「ブラウザで動くテトリスを作ってください」と、o1-previewにお願いするだけ!コードが自動生成される
HTML、CSS、JavaScriptがあっという間に作られました。VSCodeで実行
生成されたコードをVSCodeにコピペして実行すると…なんと動いた!
これすごすぎる!しかもブロックごとに色が違うし、バグもなし。動作もスムーズで、まさに感動の瞬間。
どんなコードが生成されたの?
以下に、生成されたHTML、CSS、JavaScriptすべてを掲載します!
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<!-- ページの文字エンコードをUTF-8に設定 -->
<meta charset="UTF-8">
<!-- ページのタイトルを「テトリス」に設定 -->
<title>テトリス</title>
<!-- 外部のCSSファイルを読み込んで、ページのデザインを指定 -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- テトリスのゲーム画面を描画するためのキャンバス要素を設定 -->
<!-- 幅240ピクセル、高さ400ピクセルに設定 -->
<canvas id="game" width="240" height="400"></canvas>
<!-- 現在のスコアを表示するための要素 -->
<div id="score">スコア: 0</div>
<!-- 外部のJavaScriptファイルを読み込んで、ゲームの動きを制御 -->
<script src="script.js"></script>
</body>
</html>
style.css
{
"folders": [
{
"path": ".." // 上の階層(親フォルダ)をプロジェクトとして参照
}
]
}
script.js
// ゲームのキャンバス要素を取得し、2D描画コンテキストを設定
const canvas = document.getElementById('game');
const context = canvas.getContext('2d');
// キャンバス内の描画サイズを拡大(20倍)
context.scale(20, 20);
// 各ブロックの形状に対応する色を定義
const colors = {
'T': '#FF0D72', // T型ブロックの色
'O': '#0DC2FF', // O型ブロックの色
'L': '#0DFF72', // L型ブロックの色
'J': '#F538FF', // J型ブロックの色
'I': '#FF8E0D', // I型ブロックの色
'S': '#FFE138', // S型ブロックの色
'Z': '#3877FF' // Z型ブロックの色
};
// ブロックの形状を定義する関数
function createPiece(type) {
if (type === 'T') {
// T型ブロックの形状
return [
[0, 1, 0],
[1, 1, 1],
[0, 0, 0],
];
} else if (type === 'O') {
// O型ブロックの形状(正方形)
return [
[1, 1],
[1, 1],
];
} else if (type === 'L') {
// L型ブロックの形状
return [
[0, 0, 1],
[1, 1, 1],
[0, 0, 0],
];
} else if (type === 'J') {
// J型ブロックの形状
return [
[1, 0, 0],
[1, 1, 1],
[0, 0, 0],
];
} else if (type === 'I') {
// I型ブロックの形状(棒状)
return [
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
];
} else if (type === 'S') {
// S型ブロックの形状
return [
[0, 1, 1],
[1, 1, 0],
[0, 0, 0],
];
} else if (type === 'Z') {
// Z型ブロックの形状
return [
[1, 1, 0],
[0, 1, 1],
[0, 0, 0],
];
}
}
// ゲームフィールド(アリーナ)の初期化
const arena = createMatrix(12, 20);
// 指定された幅と高さでフィールドを作成
function createMatrix(w, h) {
const matrix = [];
while (h--) {
// 各行を幅wの0で埋める
matrix.push(new Array(w).fill(0));
}
return matrix;
}
// ブロックの描画
function drawMatrix(matrix, offset, color) {
matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) {
// ブロックの色を設定し、キャンバス上に描画
context.fillStyle = color || value;
context.fillRect(x + offset.x, y + offset.y, 1, 1);
}
});
});
}
// ゲームフィールドとブロックを描画
function draw() {
// 背景を黒で塗りつぶす
context.fillStyle = '#000';
context.fillRect(0, 0, canvas.width, canvas.height);
// フィールドとプレイヤーのブロックを描画
drawMatrix(arena, { x: 0, y: 0 });
drawMatrix(player.matrix, player.pos, player.color);
}
// ブロックがフィールドと衝突しているかどうかを判定
function collide(arena, player) {
const [m, o] = [player.matrix, player.pos];
for (let y = 0; y < m.length; ++y) {
for (let x = 0; x < m[y].length; ++x) {
if (m[y][x] !== 0 &&
(arena[y + o.y] &&
arena[y + o.y][x + o.x]) !== 0) {
return true; // 衝突している
}
}
}
return false; // 衝突していない
}
// ブロックが固定されたらフィールドに統合
function merge(arena, player) {
player.matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) {
arena[y + player.pos.y][x + player.pos.x] = player.color;
}
});
});
}
// ラインが揃った場合に消去
function arenaSweep() {
outer: for (let y = arena.length - 1; y >= 0; --y) {
for (let x = 0; x < arena[y].length; ++x) {
if (arena[y][x] === 0) {
continue outer; // ラインが揃っていない
}
}
// ラインを消去し、スコアを更新
const row = arena.splice(y, 1)[0].fill(0);
arena.unshift(row);
++y;
player.score += 10;
updateScore();
}
}
// ブロックを自動で落下させる
function playerDrop() {
player.pos.y++; // ブロックを1段下に動かす
if (coll
ide(arena, player)) {
player.pos.y--; // 衝突した場合、位置を戻す
merge(arena, player); // フィールドにブロックを統合
playerReset(); // 新しいブロックを生成
arenaSweep(); // ラインを消去
}
dropCounter = 0;
}
// ブロックの落下速度を制御
let dropCounter = 0;
let dropInterval = 1000; // 1秒ごとにブロックが落ちる
// 前回のフレームからの経過時間を基に更新
let lastTime = 0;
function update(time = 0) {
const deltaTime = time - lastTime;
lastTime = time;
dropCounter += deltaTime;
if (dropCounter > dropInterval) {
playerDrop();
}
draw();
requestAnimationFrame(update); // フレームごとにゲームを更新
}
// 新しいブロックを生成
function playerReset() {
const pieces = 'ILJOTSZ'; // 7種類のブロック
const pieceType = pieces[pieces.length * Math.random() | 0]; // ランダムでブロックを選ぶ
player.matrix = createPiece(pieceType); // ブロックの形状を設定
player.pos.y = 0;
player.pos.x = (arena[0].length / 2 | 0) - (player.matrix[0].length / 2 | 0);
player.color = colors[pieceType]; // ブロックの色を設定
if (collide(arena, player)) {
// ゲームオーバー時にフィールドをリセット
arena.forEach(row => row.fill(0));
player.score = 0;
updateScore();
}
}
// ブロックを左右に移動
function playerMove(dir) {
player.pos.x += dir;
if (collide(arena, player)) {
player.pos.x -= dir; // 衝突したら元に戻す
}
}
// ブロックの回転処理
function rotate(matrix, dir) {
for (let y = 0; y < matrix.length; ++y) {
for (let x = 0; x < y; ++x) {
[matrix[x][y], matrix[y][x]] = [matrix[y][x], matrix[x][y]];
}
}
if (dir > 0) {
matrix.forEach(row => row.reverse());
} else {
matrix.reverse();
}
}
// プレイヤーのブロックを回転
function playerRotate(dir) {
const pos = player.pos.x;
let offset = 1;
rotate(player.matrix, dir);
while (collide(arena, player)) {
player.pos.x += offset;
offset = -(offset + (offset > 0 ? 1 : -1));
if (offset > player.matrix[0].length) {
rotate(player.matrix, -dir); // 回転を元に戻す
player.pos.x = pos;
return;
}
}
}
// スコアの更新
function updateScore() {
document.getElementById('score').innerText = `スコア: ${player.score}`;
}
// キー入力による操作
document.addEventListener('keydown', event => {
if (event.keyCode === 37) {
playerMove(-1); // 左移動
} else if (event.keyCode === 39) {
playerMove(1); // 右移動
} else if (event.keyCode === 40) {
playerDrop(); // 下移動(高速落下)
} else if (event.keyCode === 81) {
playerRotate(-1); // 左回転
} else if (event.keyCode === 87) {
playerRotate(1); // 右回転
}
});
// プレイヤーの初期設定
const player = {
pos: { x: 0, y: 0 },
matrix: null,
score: 0,
color: null
};
// ゲーム開始
playerReset();
updateScore();
update();
o1-previewでは動いたのに、gpt-4oでは動かなかった?
ここでさらに驚くべきポイントがあります。同じように「ブラウザでテトリスを作って」とgpt-4oにも指示してみたのですが、残念ながらうまく動作しませんでした…。コードは生成されるのですが、なぜか動かないんです。
しかし、o1-previewではしっかりと動作し、色付きのブロックがスムーズに落ちてくる完璧なテトリスができたのです!これには本当に感動。モデルによって動作結果に違いがあることに驚きを感じながらも、その可能性に胸が躍ります。
AIの可能性は無限大
これまでプログラミングに興味があっても、「自分には難しい…」と感じていた人でも、このようにAIがコードを生成してくれる時代が来たんです。しかも、バグなしでスムーズに動作するなんて、未来はすぐそこ!
o1-previewがここまでできるなんて、本当に驚きです。試しに同じ指示をgpt-4oでやってみたけど、動かない…。この差がまた、o1-previewのすごさを引き立てています。
おわりに
AIでテトリスを作るなんて、夢のような話が現実になる日が来るとは思いもしませんでした。これからもどんな可能性が広がっていくのか、ワクワクが止まりません。