4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

こんにちは!株式会社Relic 所属の 2025 Japan AWS Jr. Champion エンジニア、エイミ/amixedcolor です!(𝕏はこちら)

今回は、2025 Japan AWS Jr. Champions で行われている企画の1つ、「アウトプットもくもく会」に参加しています!

つい先日公開されたAWSのAgentic IDE「Kiro」を使う絶好の機会だと思ったので、早速使ってみました!

Kiroの公式発表については以下をご確認ください。

また、Kiroの最新情報は以下から確認できるようです!

使ってみる

セットアップ

https://kiro.dev/ にアクセスすると、ダウンロードボタンがありました。

スクリーンショット 2025-07-16 20.21.31.png

インストールして起動すると、SSOサインインを求められます。
今回は個人的に使っているので、GitHubでサインインしました。

VSCodeの設定をインポートできるようです。今回は純然なKiroを触ってみたいのでSkipします。

スクリーンショット 2025-07-16 20.24.12.png

続いてテーマが選べました。VSCode同様Darkテーマを使おうと思います。 "Kiro" Dark なので、少し独自デザインのようです。

スクリーンショット 2025-07-16 20.25.34.png

ターミナルから開ける設定もできるようです。おそらくVSCodeでいう code . に相当するものだと思います。今回は継続利用するか不明で、環境に影響ができるだけないようにしたいので、Skipします。

スクリーンショット 2025-07-16 20.26.40.png

いくつかの方法(フォルダ(project)を開く・リポジトリをクローンする・SSHで接続する)によって開始できるようです。3つ目のSSHは拡張機能が必要そうでした。使うとEC2に接続しながら開発できたりするんでしょうか :thinking:

今回は空のGitHubリポジトリを作成してクローンしてみたいと思います。

スクリーンショット 2025-07-16 20.28.08.png

リポジトリで作業する

リポジトリをクローンし、そのフォルダを開くとたくさんメニューが出てきました。英語のままでも良いですが、日本語にできるか探してみます。

スクリーンショット 2025-07-16 20.31.20.png

設定をみたところまだ日本語の設定はないようです。拡張機能も見てみます。

スクリーンショット 2025-07-16 20.33.32.png

おそらくVSCodeと同様の拡張機能がありました!インストールしてみます。

スクリーンショット 2025-07-16 20.34.18.png

リスタートをして・・・

スクリーンショット 2025-07-16 20.34.53.png

エディタのメニューは日本語になりましたが、Kiroの機能には影響ないようですね。エディタの拡張機能なので、当然と言えば当然の結果です。

スクリーンショット 2025-07-16 20.35.48.png

改めて、説明を読んでみます。

Vibe

Chat first, then build. Explore ideas and iterate as you discover needs.

Great For:

  • Rapid exploration and testing
  • Building when requirements are unclear
  • Implementing a task

(おそらく Vibe Coding の Vibe と同じ意味である)「バイブ」モードはまずチャットをして、それから構築をするモードとのことです。アイデアを検討し、ニーズを発見しながら繰り返しをします。

適しているのは以下とのこと。

  • 素早い探索とテスト
  • 要件が不明瞭な場合の構築
  • タスクの実装

そして2つ目のモードがあります。

Spec

Plan first, then build. Create requirements and design before coding starts.

Great for:

  • Thinking through features in-depth
  • Projects needing upfront planning
  • Building features in a structured way

「スペック」モードはまず計画をして、それから構築をするモードとのことです。コーディングを始める前に要件と設計を作成します。

適しているのは以下とのこと。

  • 機能を深く掘り下げて考える
  • 事前の計画が必要なプロジェクト
  • 構造的な方法で機能を構築する

ここで、現在の自分にどちらが適しているか考えます。「要件が不明瞭」ですが、「コーディングを始める前に要件と設計を作成」するのは魅力的に思えます。しかし「アイデアを検討」したいですね。デフォルトでもあったので、Vibeを選択してみます。

スクリーンショット 2025-07-16 20.36.58.png

コーディングを始める

チャット欄には色々ボタンがあります。

まず目についたのは右下のAutopilot。オンだとKiroが自分の代わりに変更を行うようです。

スクリーンショット 2025-07-16 20.49.52.png

選べるモデルは Claude Sonnet 4.0 か 3.7 でした。

スクリーンショット 2025-07-16 20.51.10.png

他にも画像を追加できたり、 # コマンドでは多くのコンテキストを追加できるようでした。

スクリーンショット 2025-07-16 20.53.05.png
スクリーンショット 2025-07-16 20.53.36.png

以下のように指示してみました。

落ちものゲームを作りたいです。テトリスやぷよぷよと似た原理ですが、独創性のあるシステムは特殊ながらもシンプルな「落ちたオブジェクトが消える条件」を持ち、ユーザーを楽しませます。

スクリーンショット 2025-07-16 20.56.09.png

いろいろな提案をしてくれました。自分のすきなものの1つである「色」の要素が出てきたので、そこを深掘りしてみます。

色の温度システムに発想を得て、隣り合った色が「加法混色で混ざることで絵の具のように色が変わり、別の定義済みの色にできる」場合は色が変わるのはいかがでしょうか?そこにユニークな「消える条件」を提案して欲しいです。

スクリーンショット 2025-07-16 21.00.37.png

基本色と混合色がいい塩梅です。

「光」に着想を得て、白色を作れたら消えるというのはいかがでしょうか?ただ白色なだけでなく、何か追加の条件を適度に設けて楽しくしつつ、混合色同士も混合することで基本職に戻せるようなシステムはいかがでしょうか?

スクリーンショット 2025-07-16 21.03.42.png

ちょっとプレイ難易度は高そうですが、面白そうなので作ってもらいます。

面白そうです!それで作成してください。

スクリーンショット 2025-07-16 21.05.44.png

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>光の合成パズル</title>
    <style>
        body {
            margin: 0;
            padding: 20px;
            background: #1a1a2e;
            color: white;
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
        }
        
        .game-container {
            display: flex;
            gap: 30px;
            align-items: flex-start;
        }
        
        .game-board {
            border: 3px solid #16213e;
            background: #0f0f23;
            position: relative;
        }
        
        .cell {
            width: 30px;
            height: 30px;
            border: 1px solid #333;
            display: inline-block;
            position: absolute;
            transition: all 0.3s ease;
        }
        
        .red { background: #ff4444; }
        .green { background: #44ff44; }
        .blue { background: #4444ff; }
        .yellow { background: #ffff44; }
        .purple { background: #ff44ff; }
        .cyan { background: #44ffff; }
        .white { 
            background: #ffffff; 
            box-shadow: 0 0 10px #ffffff;
        }
        
        .info-panel {
            background: #16213e;
            padding: 20px;
            border-radius: 10px;
            min-width: 200px;
        }
        
        .next-piece {
            margin: 20px 0;
        }
        
        .controls {
            margin-top: 20px;
            font-size: 14px;
            line-height: 1.6;
        }
        
        .score {
            font-size: 24px;
            margin-bottom: 20px;
        }
        
        .game-over {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0,0,0,0.9);
            padding: 30px;
            border-radius: 10px;
            text-align: center;
            display: none;
        }
    </style>
</head>
<body>
    <div class="game-container">
        <div class="game-board" id="gameBoard"></div>
        <div class="info-panel">
            <div class="score">スコア: <span id="score">0</span></div>
            <div class="next-piece">
                <h3>次のピース:</h3>
                <div id="nextPiece"></div>
            </div>
            <div class="controls">
                <h3>操作方法:</h3>
                <p>← → : 移動</p>
                <p>↓ : 高速落下</p>
                <p>スペース : 回転</p>
                <br>
                <h3>ルール:</h3>
                <p>• R+G = 黄色</p>
                <p>• R+B = 紫色</p>
                <p>• G+B = 水色</p>
                <p>• R+G+B = 白色</p>
                <p>• 白色を中心に十字形で同色4つが揃うと消える</p>
                <p>• 混合色同士が隣接すると基本色に分解</p>
            </div>
        </div>
    </div>
    
    <div class="game-over" id="gameOver">
        <h2>ゲームオーバー</h2>
        <p>最終スコア: <span id="finalScore">0</span></p>
        <button onclick="restartGame()">もう一度プレイ</button>
    </div>

    <script src="game.js"></script>
</body>
</html>

game.js

// ゲーム設定
const BOARD_WIDTH = 10;
const BOARD_HEIGHT = 20;
const CELL_SIZE = 30;

// 色の定義
const COLORS = {
    EMPTY: 0,
    RED: 1,
    GREEN: 2,
    BLUE: 3,
    YELLOW: 4,  // RED + GREEN
    PURPLE: 5,  // RED + BLUE
    CYAN: 6,    // GREEN + BLUE
    WHITE: 7    // RED + GREEN + BLUE
};

const COLOR_CLASSES = {
    [COLORS.EMPTY]: '',
    [COLORS.RED]: 'red',
    [COLORS.GREEN]: 'green',
    [COLORS.BLUE]: 'blue',
    [COLORS.YELLOW]: 'yellow',
    [COLORS.PURPLE]: 'purple',
    [COLORS.CYAN]: 'cyan',
    [COLORS.WHITE]: 'white'
};

// ゲーム状態
let board = [];
let currentPiece = null;
let nextPiece = null;
let score = 0;
let gameRunning = true;
let dropTimer = 0;
let dropInterval = 1000; // 1秒

// 初期化
function initGame() {
    // ボードの初期化
    board = Array(BOARD_HEIGHT).fill().map(() => Array(BOARD_WIDTH).fill(COLORS.EMPTY));
    
    // ゲームボードのDOM作成
    const gameBoard = document.getElementById('gameBoard');
    gameBoard.style.width = (BOARD_WIDTH * (CELL_SIZE + 2)) + 'px';
    gameBoard.style.height = (BOARD_HEIGHT * (CELL_SIZE + 2)) + 'px';
    gameBoard.innerHTML = '';
    
    // セルの作成
    for (let y = 0; y < BOARD_HEIGHT; y++) {
        for (let x = 0; x < BOARD_WIDTH; x++) {
            const cell = document.createElement('div');
            cell.className = 'cell';
            cell.style.left = (x * (CELL_SIZE + 2)) + 'px';
            cell.style.top = (y * (CELL_SIZE + 2)) + 'px';
            cell.id = `cell-${x}-${y}`;
            gameBoard.appendChild(cell);
        }
    }
    
    // 最初のピース生成
    nextPiece = generatePiece();
    spawnNewPiece();
    updateDisplay();
    
    // ゲームループ開始
    gameLoop();
}

// ピース生成(基本色のみ)
function generatePiece() {
    const basicColors = [COLORS.RED, COLORS.GREEN, COLORS.BLUE];
    return {
        color: basicColors[Math.floor(Math.random() * basicColors.length)],
        x: Math.floor(BOARD_WIDTH / 2),
        y: 0
    };
}

// 新しいピースをスポーン
function spawnNewPiece() {
    currentPiece = nextPiece;
    nextPiece = generatePiece();
    
    // ゲームオーバー判定
    if (board[currentPiece.y][currentPiece.x] !== COLORS.EMPTY) {
        gameOver();
        return;
    }
    
    updateNextPieceDisplay();
}

// 次のピース表示更新
function updateNextPieceDisplay() {
    const nextPieceDiv = document.getElementById('nextPiece');
    nextPieceDiv.innerHTML = `<div class="cell ${COLOR_CLASSES[nextPiece.color]}" style="position: relative; margin: 5px;"></div>`;
}

// ピースの移動可能性チェック
function canMoveTo(x, y) {
    return x >= 0 && x < BOARD_WIDTH && 
           y >= 0 && y < BOARD_HEIGHT && 
           board[y][x] === COLORS.EMPTY;
}

// ピースの移動
function movePiece(dx, dy) {
    if (!currentPiece || !gameRunning) return false;
    
    const newX = currentPiece.x + dx;
    const newY = currentPiece.y + dy;
    
    if (canMoveTo(newX, newY)) {
        currentPiece.x = newX;
        currentPiece.y = newY;
        updateDisplay();
        return true;
    }
    return false;
}

// ピースの固定
function lockPiece() {
    if (!currentPiece) return;
    
    board[currentPiece.y][currentPiece.x] = currentPiece.color;
    
    // 色の混合処理
    processColorMixing(currentPiece.x, currentPiece.y);
    
    // 消去判定
    processElimination();
    
    // 新しいピースをスポーン
    spawnNewPiece();
    updateDisplay();
}

// 色の混合処理
function processColorMixing(x, y) {
    const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
    const currentColor = board[y][x];
    
    for (const [dx, dy] of directions) {
        const nx = x + dx;
        const ny = y + dy;
        
        if (nx >= 0 && nx < BOARD_WIDTH && ny >= 0 && ny < BOARD_HEIGHT) {
            const neighborColor = board[ny][nx];
            
            if (neighborColor !== COLORS.EMPTY) {
                const mixedColor = mixColors(currentColor, neighborColor);
                if (mixedColor && mixedColor !== currentColor) {
                    board[y][x] = mixedColor;
                    board[ny][nx] = COLORS.EMPTY; // 隣接色は消える
                    return; // 一度に一つの混合のみ
                }
            }
        }
    }
}

// 色の混合ルール
function mixColors(color1, color2) {
    // 基本色の混合
    if ((color1 === COLORS.RED && color2 === COLORS.GREEN) || 
        (color1 === COLORS.GREEN && color2 === COLORS.RED)) {
        return COLORS.YELLOW;
    }
    if ((color1 === COLORS.RED && color2 === COLORS.BLUE) || 
        (color1 === COLORS.BLUE && color2 === COLORS.RED)) {
        return COLORS.PURPLE;
    }
    if ((color1 === COLORS.GREEN && color2 === COLORS.BLUE) || 
        (color1 === COLORS.BLUE && color2 === COLORS.GREEN)) {
        return COLORS.CYAN;
    }
    
    // 白色の生成
    if ((color1 === COLORS.YELLOW && color2 === COLORS.BLUE) || 
        (color1 === COLORS.BLUE && color2 === COLORS.YELLOW)) {
        return COLORS.WHITE;
    }
    if ((color1 === COLORS.PURPLE && color2 === COLORS.GREEN) || 
        (color1 === COLORS.GREEN && color2 === COLORS.PURPLE)) {
        return COLORS.WHITE;
    }
    if ((color1 === COLORS.CYAN && color2 === COLORS.RED) || 
        (color1 === COLORS.RED && color2 === COLORS.CYAN)) {
        return COLORS.WHITE;
    }
    
    return null; // 混合しない
}

// 消去処理
function processElimination() {
    let eliminated = false;
    
    // 白色を中心とした十字形の消去をチェック
    for (let y = 1; y < BOARD_HEIGHT - 1; y++) {
        for (let x = 1; x < BOARD_WIDTH - 1; x++) {
            if (board[y][x] === COLORS.WHITE) {
                const up = board[y-1][x];
                const down = board[y+1][x];
                const left = board[y][x-1];
                const right = board[y][x+1];
                
                // 十字形で同色が4つ揃っているかチェック
                if (up !== COLORS.EMPTY && up === down && up === left && up === right) {
                    // 消去
                    board[y][x] = COLORS.EMPTY;
                    board[y-1][x] = COLORS.EMPTY;
                    board[y+1][x] = COLORS.EMPTY;
                    board[y][x-1] = COLORS.EMPTY;
                    board[y][x+1] = COLORS.EMPTY;
                    
                    score += 100;
                    eliminated = true;
                }
            }
        }
    }
    
    if (eliminated) {
        // 重力処理
        applyGravity();
        // 再帰的に消去処理(連鎖)
        setTimeout(() => processElimination(), 300);
    }
}

// 重力処理
function applyGravity() {
    for (let x = 0; x < BOARD_WIDTH; x++) {
        let writePos = BOARD_HEIGHT - 1;
        for (let y = BOARD_HEIGHT - 1; y >= 0; y--) {
            if (board[y][x] !== COLORS.EMPTY) {
                board[writePos][x] = board[y][x];
                if (writePos !== y) {
                    board[y][x] = COLORS.EMPTY;
                }
                writePos--;
            }
        }
    }
}

// 表示更新
function updateDisplay() {
    // ボードの表示更新
    for (let y = 0; y < BOARD_HEIGHT; y++) {
        for (let x = 0; x < BOARD_WIDTH; x++) {
            const cell = document.getElementById(`cell-${x}-${y}`);
            cell.className = `cell ${COLOR_CLASSES[board[y][x]]}`;
        }
    }
    
    // 現在のピースの表示
    if (currentPiece && gameRunning) {
        const pieceCell = document.getElementById(`cell-${currentPiece.x}-${currentPiece.y}`);
        if (pieceCell) {
            pieceCell.className = `cell ${COLOR_CLASSES[currentPiece.color]}`;
        }
    }
    
    // スコア更新
    document.getElementById('score').textContent = score;
}

// ゲームループ
function gameLoop() {
    if (!gameRunning) return;
    
    dropTimer += 16; // 約60FPS
    
    if (dropTimer >= dropInterval) {
        if (!movePiece(0, 1)) {
            lockPiece();
        }
        dropTimer = 0;
    }
    
    requestAnimationFrame(gameLoop);
}

// キーボード入力
document.addEventListener('keydown', (e) => {
    if (!gameRunning) return;
    
    switch(e.key) {
        case 'ArrowLeft':
            movePiece(-1, 0);
            e.preventDefault();
            break;
        case 'ArrowRight':
            movePiece(1, 0);
            e.preventDefault();
            break;
        case 'ArrowDown':
            if (movePiece(0, 1)) {
                score += 1;
            }
            e.preventDefault();
            break;
        case ' ':
            // 回転は今回は実装しない(単一ブロックのため)
            e.preventDefault();
            break;
    }
});

// ゲームオーバー
function gameOver() {
    gameRunning = false;
    document.getElementById('finalScore').textContent = score;
    document.getElementById('gameOver').style.display = 'block';
}

// ゲーム再開
function restartGame() {
    score = 0;
    gameRunning = true;
    document.getElementById('gameOver').style.display = 'none';
    initGame();
}

// ゲーム開始
initGame();

2分くらいでできました。結構早い?

動かしてみたところ、消える処理はうまくできているようでした。
また、混合色もたしかに生成できました!
ただ、R+G+Bが混合色+残りの1色として実現されていて、少しイメージとは異なりました。

スクリーンショット 2025-07-16 21.18.47.png

スクリーンショット 2025-07-16 21.19.16.png

いずれにせよ、一発でエラーなくhtmlとjsのゲームは作れそうです。まだKiroならではの強みはわからないですが…。

おわりに

曖昧な指示の割には、形にしてくれたゲームは予想以上に品質が高く、白いオブジェクトの発光感まで出してくれるとはびっくりしました。まだまだ、それはもう0.01%くらいしか力を発揮させられていないと思いますが、ストレスフリーに使い始めることができました。

Kiroが気になる方はぜひ、まずゲームなどを作らせてみても面白いと思います。

ここまでお読みいただきありがとうございました!
エイミでした。それでは、またこんど!

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?