😸

claude codeを使って立方体を回転するコード作らせた

に公開2

今回はタイトル通り、claude codeを使ったらコンソール画面で立方体を回転させることができるか試してみました。

早速使ってみる

環境構築

まずはclaude codeを実行させるための環境を用意しました。

uv init python_3d_console -p 3.12
cd python_3d_console

claude codeログ記録について

私は普段claude codeのやり取りを保存するために以下のスラッシュコマンドを自作して使ってます。使い方はマークダウン通りで/log_chatコマンドを使います。指定したファイルにプロンプトやclaude codeの思考の履歴を残すことができます。

~/.claude/commands/log_chat.md
# 目的
- このコマンドが実行された後のチャットのやり取りを全てマークダウン形式で記録すること

# 利用方法
## コマンド
/log_chat startと入力された後のやり取りを全てログファイルに保存する
/log_chat endと入力されるとファイルへの記録を終了する

## 保存される情報
- チャット履歴:ユーザからの入力、LLMの思考過程、LLMの出力
- トークン使用量について
  - /log_chat endが入力されてから計算して記述すること

## オプション
- 第一引数:ファイル名
  - ファイル名が指定された場合、そのファイルに記録すること
  - 指定されなかった場合、log.mdに保存すること
- --save-thinkingが指定された場合入出力だけでなく、あなたの思考プロセスも記録すること

コードを生成させてみる

今回は以下のプロンプトを連続して指定しました。

  • Pythonを使って、コンソール上で立方体がぐるぐる回るコードを実装して
  • 立方体の各面にはそれぞれ異なる記号で埋め尽くしてください。また、回転は滑らかに指定した秒数指定した速さで回転するようにして

今結果生成されたコードは以下になります。とても行数はありますが、立方体の回転は回転行列を計算して実行されており、三角関数の組み合わせで実装されています。また回転のスピードや実行時間はコマンドライン引数で指定できるようになっています。

コード実装です
rotate_cube.py
#!/usr/bin/env python3
import math
import time
import os
import sys
import argparse

class RotatingCube:
    def __init__(self, duration=None, speed=1.0):
        # Cube vertices in 3D space
        self.vertices = [
            [-1, -1, -1],  # 0
            [1, -1, -1],   # 1
            [1, 1, -1],    # 2
            [-1, 1, -1],   # 3
            [-1, -1, 1],   # 4
            [1, -1, 1],    # 5
            [1, 1, 1],     # 6
            [-1, 1, 1]     # 7
        ]
        
        # Define faces with their vertices (counter-clockwise when viewed from outside)
        self.faces = [
            {'vertices': [0, 1, 2, 3], 'symbol': '#', 'name': 'back'},    # Back face
            {'vertices': [4, 7, 6, 5], 'symbol': '@', 'name': 'front'},   # Front face
            {'vertices': [0, 4, 5, 1], 'symbol': '*', 'name': 'bottom'},  # Bottom face
            {'vertices': [2, 6, 7, 3], 'symbol': '+', 'name': 'top'},     # Top face
            {'vertices': [0, 3, 7, 4], 'symbol': '%', 'name': 'left'},    # Left face
            {'vertices': [1, 5, 6, 2], 'symbol': '&', 'name': 'right'}    # Right face
        ]
        
        # Define edges connecting vertices
        self.edges = [
            [0, 1], [1, 2], [2, 3], [3, 0],  # Back face
            [4, 5], [5, 6], [6, 7], [7, 4],  # Front face
            [0, 4], [1, 5], [2, 6], [3, 7]   # Connecting edges
        ]
        
        # Rotation angles
        self.angle_x = 0
        self.angle_y = 0
        self.angle_z = 0
        
        # Animation parameters
        self.duration = duration  # None means infinite
        self.speed = speed       # Rotation speed multiplier
        self.start_time = time.time()
        
        # Screen dimensions
        self.width = 80
        self.height = 30
        self.scale = 8
        
        # Z-buffer for hidden surface removal
        self.z_buffer = None
        
    def rotate_x(self, point, angle):
        """Rotate point around X axis"""
        cos_a = math.cos(angle)
        sin_a = math.sin(angle)
        y = point[1] * cos_a - point[2] * sin_a
        z = point[1] * sin_a + point[2] * cos_a
        return [point[0], y, z]
    
    def rotate_y(self, point, angle):
        """Rotate point around Y axis"""
        cos_a = math.cos(angle)
        sin_a = math.sin(angle)
        x = point[0] * cos_a + point[2] * sin_a
        z = -point[0] * sin_a + point[2] * cos_a
        return [x, point[1], z]
    
    def rotate_z(self, point, angle):
        """Rotate point around Z axis"""
        cos_a = math.cos(angle)
        sin_a = math.sin(angle)
        x = point[0] * cos_a - point[1] * sin_a
        y = point[0] * sin_a + point[1] * cos_a
        return [x, y, point[2]]
    
    def project_3d_to_2d(self, point):
        """Project 3D point to 2D screen coordinates"""
        # Simple perspective projection
        distance = 5
        factor = distance / (distance + point[2])
        x = int(point[0] * factor * self.scale + self.width / 2)
        y = int(point[1] * factor * self.scale + self.height / 2)
        return [x, y]
    
    def clear_screen(self):
        """Clear the console screen"""
        os.system('cls' if os.name == 'nt' else 'clear')
    
    def draw_line(self, screen, p1, p2, z1, z2, char='*'):
        """Draw a line between two points using Bresenham's algorithm with z-buffer"""
        x1, y1 = p1
        x2, y2 = p2
        
        dx = abs(x2 - x1)
        dy = abs(y2 - y1)
        
        x_step = 1 if x1 < x2 else -1
        y_step = 1 if y1 < y2 else -1
        
        error = dx - dy
        
        # Calculate total steps for z interpolation
        steps = max(dx, dy)
        if steps == 0:
            steps = 1
        
        current_step = 0
        
        while True:
            if 0 <= x1 < self.width and 0 <= y1 < self.height:
                # Interpolate z value
                t = current_step / steps if steps > 0 else 0
                z = z1 + (z2 - z1) * t
                
                # Only draw if closer than what's in z-buffer
                if z > self.z_buffer[y1][x1]:
                    screen[y1][x1] = char
                    self.z_buffer[y1][x1] = z
            
            if x1 == x2 and y1 == y2:
                break
                
            error2 = 2 * error
            
            if error2 > -dy:
                error -= dy
                x1 += x_step
            
            if error2 < dx:
                error += dx
                y1 += y_step
            
            current_step += 1
    
    def cross_product(self, v1, v2):
        """Calculate cross product of two 3D vectors"""
        return [
            v1[1] * v2[2] - v1[2] * v2[1],
            v1[2] * v2[0] - v1[0] * v2[2],
            v1[0] * v2[1] - v1[1] * v2[0]
        ]
    
    def dot_product(self, v1, v2):
        """Calculate dot product of two 3D vectors"""
        return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]
    
    def is_point_in_triangle(self, p, a, b, c):
        """Check if point p is inside triangle abc using barycentric coordinates"""
        # Vectors
        v0 = [c[0] - a[0], c[1] - a[1]]
        v1 = [b[0] - a[0], b[1] - a[1]]
        v2 = [p[0] - a[0], p[1] - a[1]]
        
        # Compute dot products
        dot00 = v0[0] * v0[0] + v0[1] * v0[1]
        dot01 = v0[0] * v1[0] + v0[1] * v1[1]
        dot02 = v0[0] * v2[0] + v0[1] * v2[1]
        dot11 = v1[0] * v1[0] + v1[1] * v1[1]
        dot12 = v1[0] * v2[0] + v1[1] * v2[1]
        
        # Compute barycentric coordinates
        denom = (dot00 * dot11 - dot01 * dot01)
        if abs(denom) < 1e-10:  # Degenerate triangle
            return False
        
        inv_denom = 1 / denom
        u = (dot11 * dot02 - dot01 * dot12) * inv_denom
        v = (dot00 * dot12 - dot01 * dot02) * inv_denom
        
        # Check if point is in triangle
        return (u >= 0) and (v >= 0) and (u + v <= 1)
    
    def fill_face(self, screen, vertices_2d, vertices_3d, symbol):
        """Fill a face with the given symbol"""
        # Find bounding box
        min_x = max(0, min(v[0] for v in vertices_2d))
        max_x = min(self.width - 1, max(v[0] for v in vertices_2d))
        min_y = max(0, min(v[1] for v in vertices_2d))
        max_y = min(self.height - 1, max(v[1] for v in vertices_2d))
        
        # Calculate average z for the face
        avg_z = sum(v[2] for v in vertices_3d) / len(vertices_3d)
        
        # Scan through the bounding box
        for y in range(int(min_y), int(max_y) + 1):
            for x in range(int(min_x), int(max_x) + 1):
                point = [x, y]
                
                # Check if point is inside the polygon using triangulation
                # Split quad into two triangles
                if len(vertices_2d) == 4:
                    in_triangle1 = self.is_point_in_triangle(point, vertices_2d[0], vertices_2d[1], vertices_2d[2])
                    in_triangle2 = self.is_point_in_triangle(point, vertices_2d[0], vertices_2d[2], vertices_2d[3])
                    
                    if (in_triangle1 or in_triangle2) and avg_z > self.z_buffer[y][x]:
                        screen[y][x] = symbol
                        self.z_buffer[y][x] = avg_z
    
    def render(self):
        """Render the rotating cube"""
        # Create empty screen buffer and z-buffer
        screen = [[' ' for _ in range(self.width)] for _ in range(self.height)]
        self.z_buffer = [[-float('inf') for _ in range(self.width)] for _ in range(self.height)]
        
        # Transform vertices
        transformed_vertices_3d = []
        transformed_vertices_2d = []
        
        for vertex in self.vertices:
            # Apply rotations
            point = vertex.copy()
            point = self.rotate_x(point, self.angle_x)
            point = self.rotate_y(point, self.angle_y)
            point = self.rotate_z(point, self.angle_z)
            transformed_vertices_3d.append(point)
            
            # Project to 2D
            point_2d = self.project_3d_to_2d(point)
            transformed_vertices_2d.append(point_2d)
        
        # Sort faces by average z-coordinate (back to front)
        face_order = []
        for i, face in enumerate(self.faces):
            # Calculate face center z-coordinate
            face_z = sum(transformed_vertices_3d[v][2] for v in face['vertices']) / 4
            face_order.append((i, face_z))
        
        face_order.sort(key=lambda x: x[1])
        
        # Draw faces (back to front)
        for face_idx, _ in face_order:
            face = self.faces[face_idx]
            face_vertices_2d = [transformed_vertices_2d[v] for v in face['vertices']]
            face_vertices_3d = [transformed_vertices_3d[v] for v in face['vertices']]
            
            # Check if face is visible (backface culling)
            # Calculate face normal
            v1 = [face_vertices_3d[1][i] - face_vertices_3d[0][i] for i in range(3)]
            v2 = [face_vertices_3d[2][i] - face_vertices_3d[0][i] for i in range(3)]
            normal = self.cross_product(v1, v2)
            
            # View vector (from face center to viewer)
            face_center = [sum(v[i] for v in face_vertices_3d) / 4 for i in range(3)]
            view_vector = [0 - face_center[0], 0 - face_center[1], 5 - face_center[2]]
            
            # If dot product is positive, face is visible
            if self.dot_product(normal, view_vector) > 0:
                self.fill_face(screen, face_vertices_2d, face_vertices_3d, face['symbol'])
        
        # Draw edges on top
        for edge in self.edges:
            p1 = transformed_vertices_2d[edge[0]]
            p2 = transformed_vertices_2d[edge[1]]
            z1 = transformed_vertices_3d[edge[0]][2]
            z2 = transformed_vertices_3d[edge[1]][2]
            self.draw_line(screen, p1, p2, z1, z2, 'o')
        
        # Print screen with frame info
        self.clear_screen()
        
        # Display rotation info
        elapsed = time.time() - self.start_time
        if self.duration:
            remaining = max(0, self.duration - elapsed)
            print(f"Rotation Speed: {self.speed:.1f}x | Time Remaining: {remaining:.1f}s")
        else:
            print(f"Rotation Speed: {self.speed:.1f}x | Elapsed: {elapsed:.1f}s")
        
        print("─" * self.width)
        
        for row in screen:
            print(''.join(row))
        
        print("─" * self.width)
        print("Face Symbols: # = back, @ = front, * = bottom, + = top, % = left, & = right")
        print("Press Ctrl+C to stop")
    
    def animate(self):
        """Main animation loop"""
        try:
            while True:
                # Check if duration has elapsed
                if self.duration and (time.time() - self.start_time) >= self.duration:
                    print("\nAnimation completed.")
                    break
                
                self.render()
                
                # Update rotation angles with speed multiplier
                self.angle_x += 0.02 * self.speed
                self.angle_y += 0.03 * self.speed
                self.angle_z += 0.01 * self.speed
                
                # Control frame rate for smooth animation
                time.sleep(0.033)  # ~30 FPS
                
        except KeyboardInterrupt:
            print("\nAnimation stopped.")
            sys.exit(0)

def main():
    parser = argparse.ArgumentParser(description='Display a rotating 3D cube in the console')
    parser.add_argument('-d', '--duration', type=float, default=None,
                        help='Duration of animation in seconds (default: infinite)')
    parser.add_argument('-s', '--speed', type=float, default=1.0,
                        help='Rotation speed multiplier (default: 1.0)')
    
    args = parser.parse_args()
    
    if args.speed <= 0:
        print("Error: Speed must be greater than 0")
        sys.exit(1)
    
    if args.duration and args.duration <= 0:
        print("Error: Duration must be greater than 0")
        sys.exit(1)
    
    cube = RotatingCube(duration=args.duration, speed=args.speed)
    print(f"Starting cube animation...")
    if args.duration:
        print(f"Duration: {args.duration} seconds")
    print(f"Speed: {args.speed}x")
    print("Press Ctrl+C to stop")
    time.sleep(2)
    cube.animate()

if __name__ == "__main__":
    main()

ちなみに、自動記録されたログは以下になります。

claude codeログです
# Chat History Log

Started: 2025-07-17

## Command Execution
- Command: `/log_chat history.md --save-thinking`
- Purpose: Log all chat interactions including thinking processes

---

### User Input
立方体の各面にはそれぞれ異なる記号で埋め尽くしてください。また、回転は滑らかに指定した秒数指定した速さで回転するようにして

### Assistant Thinking
The user is asking me to:
1. Fill each face of the cube with different symbols (not just edges)
2. Make the rotation smooth with specified duration and speed

I need to:
1. Implement face filling algorithm (fill the interior of each face with its symbol)
2. Add support for rotation duration and speed parameters
3. Implement hidden surface removal so that only visible faces are drawn
4. Add command-line arguments for duration and speed control

Key changes needed:
- Add face definitions with symbols
- Implement polygon filling algorithm
- Add z-buffer for proper face ordering
- Add argparse for command-line arguments
- Update animation loop to respect duration and speed

### Assistant Response
I'll modify the code to fill each face of the cube with different symbols and add smooth rotation with configurable duration and speed.

[Modified rotating_cube.py with the following improvements:
- Added face definitions with unique symbols (# = back, @ = front, * = bottom, + = top, % = left, & = right)
- Implemented polygon filling algorithm using barycentric coordinates
- Added z-buffer for hidden surface removal
- Added command-line arguments for duration (-d) and speed (-s)
- Fixed division by zero error in triangle detection
- Smooth 30 FPS animation with configurable speed multiplier]

[Ran the script with 10 seconds duration and 1.5x speed - successfully displayed a rotating cube with filled faces]

立方体の各面を異なる記号で埋め尽くし、滑らかな回転を実装しました。

使い方:
- `python rotating_cube.py` - 無限に回転
- `python rotating_cube.py -d 10 -s 2.0` - 10秒間、2倍速で回転
- 各面の記号: # = 背面, @ = 前面, * = 下面, + = 上面, % = 左面, & = 右面

---

## Chat Log

### User Input
/log_chat history.md --save-thinking

### Assistant Thinking
The user has executed the `/log_chat` command with arguments `history.md --save-thinking`. Based on the command documentation, this means:

1. I should start logging all chat interactions from now on
2. The log should be saved to `history.md` file  
3. The `--save-thinking` option means I should include my thinking process in the log
4. The log should be in markdown format
5. When `/log_chat end` is entered, I should stop logging and calculate token usage

I need to:
1. Create or prepare the `history.md` file
2. Start logging this conversation
3. Include my thinking process since `--save-thinking` was specified

### Assistant Response
I'll start logging the chat history to `history.md` with thinking processes included.

[Created history.md file to log the conversation]

Logging started. All subsequent chat interactions will be recorded in `history.md` including thinking processes.

---

### User Input
立方体の各面にはそれぞれ異なる記号で埋め尽くしてください。また、回転は滑らかに指定した秒数指定した速さで回転するようにして

### Assistant Thinking
The user is asking me to:
1. Fill each face of the cube with different symbols (not just edges)
2. Make the rotation smooth with specified duration and speed

I need to:
1. Implement face filling algorithm (fill the interior of each face with its symbol)
2. Add support for rotation duration and speed parameters
3. Implement hidden surface removal so that only visible faces are drawn
4. Add command-line arguments for duration and speed control

Key changes needed:
- Add face definitions with symbols
- Implement polygon filling algorithm
- Add z-buffer for proper face ordering
- Add argparse for command-line arguments
- Update animation loop to respect duration and speed

### Assistant Response
I'll modify the code to fill each face of the cube with different symbols and add smooth rotation with configurable duration and speed.

[Modified rotating_cube.py with the following improvements:
- Added face definitions with unique symbols (# = back, @ = front, * = bottom, + = top, % = left, & = right)
- Implemented polygon filling algorithm using barycentric coordinates
- Added z-buffer for hidden surface removal
- Added command-line arguments for duration (-d) and speed (-s)
- Fixed division by zero error in triangle detection
- Smooth 30 FPS animation with configurable speed multiplier]

[Ran the script with 10 seconds duration and 1.5x speed - successfully displayed a rotating cube with filled faces]

立方体の各面を異なる記号で埋め尽くし、滑らかな回転を実装しました。

使い方:
- `python rotating_cube.py` - 無限に回転
- `python rotating_cube.py -d 10 -s 2.0` - 10秒間、2倍速で回転
- 各面の記号: # = 背面, @ = 前面, * = 下面, + = 上面, % = 左面, & = 右面

---

### User Input
Pythonを使って、コンソール上で立方体がぐるぐる回るコードを実装して

### Assistant Thinking
The user is asking me to implement a Python code that displays a rotating cube in the console. I need to:

1. Create a 3D cube representation with vertices and edges
2. Implement 3D rotation transformations (X, Y, Z axes)
3. Project 3D coordinates to 2D for console display
4. Use ASCII characters to draw the cube
5. Create an animation loop that continuously rotates and redraws the cube

I'll create a Python script that:
- Defines cube vertices in 3D space
- Implements rotation matrices for 3D transformations
- Projects 3D points to 2D screen coordinates
- Draws lines between vertices to form edges
- Animates the rotation in a loop

### Assistant Response
I'll create a Python script that displays a rotating cube in the console using ASCII characters.

[Created rotating_cube.py file with the following features:
- 3D cube with 8 vertices and 12 edges
- Rotation around X, Y, and Z axes
- 3D to 2D projection
- ASCII line drawing using Bresenham's algorithm
- Animation loop with continuous rotation]

[Ran the script and it successfully displayed a rotating cube animation in the console]

コンソール上で立方体が回転するPythonスクリプトを作成しました。`rotating_cube.py`を実行すると、ASCIIアートで描かれた立方体がぐるぐる回転します。Ctrl+Cで停止できます。

---

### User Input
立方体の各面にはそれぞれ異なる記号で埋め尽くしてください。また、回転は滑らかに指定した秒数指定した速さで回転するようにして

### Assistant Thinking
The user is asking me to:
1. Fill each face of the cube with different symbols (not just edges)
2. Make the rotation smooth with specified duration and speed

I need to:
1. Implement face filling algorithm (fill the interior of each face with its symbol)
2. Add support for rotation duration and speed parameters
3. Implement hidden surface removal so that only visible faces are drawn
4. Add command-line arguments for duration and speed control

Key changes needed:
- Add face definitions with symbols
- Implement polygon filling algorithm
- Add z-buffer for proper face ordering
- Add argparse for command-line arguments
- Update animation loop to respect duration and speed

### Assistant Response
I'll modify the code to fill each face of the cube with different symbols and add smooth rotation with configurable duration and speed.

[Modified rotating_cube.py with the following improvements:
- Added face definitions with unique symbols (# = back, @ = front, * = bottom, + = top, % = left, & = right)
- Implemented polygon filling algorithm using barycentric coordinates
- Added z-buffer for hidden surface removal
- Added command-line arguments for duration (-d) and speed (-s)
- Fixed division by zero error in triangle detection
- Smooth 30 FPS animation with configurable speed multiplier]

[Ran the script with 10 seconds duration and 1.5x speed - successfully displayed a rotating cube with filled faces]

立方体の各面を異なる記号で埋め尽くし、滑らかな回転を実装しました。

使い方:
- `python rotating_cube.py` - 無限に回転
- `python rotating_cube.py -d 10 -s 2.0` - 10秒間、2倍速で回転
- 各面の記号: # = 背面, @ = 前面, * = 下面, + = 上面, % = 左面, & = 右面

---


---

### User Input
/log_chat end

### Assistant Thinking
The user has executed `/log_chat end`, which means I need to:
1. Stop logging the chat history
2. Add the final user input to the log
3. Calculate token usage for the conversation
4. Add a summary section with token usage information

Let me update the history.md file with the final entries and token usage information.

### Assistant Response
ログの記録を終了しました。

---

## Summary

### Session Information
- Start Time: 2025-07-17
- Log File: history.md
- Options: --save-thinking (思考プロセスを含む)

### Token Usage
- Total Input Tokens: ~8,500
- Total Output Tokens: ~12,000
- Total Tokens: ~20,500

### Tasks Completed
1. Pythonで立方体が回転するコンソールアプリケーションを実装
2. 各面を異なる記号で塗りつぶす機能を追加
3. 回転速度と時間を指定できるコマンドライン引数を実装

### Files Created
- `rotating_cube.py` - 3D立方体回転アニメーションスクリプト
- `history.md` - チャット履歴ログファイル

実行結果

それでは実行してみます。結果は以下のように画面上で立方体がくるくる回るコードが実装できたことが確認できました。

uv run rotate_cube.py --duration 10.0 --speed 2.0

まとめ

今回はclaude codeの実力を試すために立方体をコマンドラインでくるくる回すコードを実装してみました。おそらくできるだろうなとは思っていましたが、思ったようにちゃんと実装されていて生成AIのコーディング能力は素晴らしいと感じました。ぜひ皆さんも色々な実装をさせてみてください。

Discussion

マッサン (Masanori Yoshida)マッサン (Masanori Yoshida)

めちゃグルグル回ってますね!

AkasanAkasan

コメントありがとうございます!
Zennに載せれるgifのサイズの関係で間引いた結果すごい早く回ってますが、パラメータ設定すればもっと爆速で回りますw