Cursor触ってみるメモ
こんな事を書いたので自分で手を動かして見ていこうと思う
Cursorは1年前の時点で友人からCopilotとレベルが違うヤバいってオススメされてたので気にはなってた
とりあえずインストール
こちらの記事が最近のインストーラー事情と近かったので参考に
VSCodeにインストール済みの拡張機能を持ってくることが出来るけどやらないほうが良いかも(対応してないのもあって無効化が面倒だった
Cursorアカウントのログインを求められたのでアカウント作成
とりあえずGoogleアカウントで作成したが14日間Proアカウントをトライアルできるっぽい
チャット出力はgpt-4o-mini or cursor-small は無制限で使えてそれ以外のモデルは250回まで使えるっぽい
Proアカウントは月額20ドルで、この回数は多いのか少ないのかわからない
WSLへの接続は多分VSCodeの拡張機能使いまわして簡単にできた
ctrl+lでチャットが開ける
Cursorのチャット上のパスはプロジェクトのルートパスを示してくれなかったので注意
スロットマシーンをjsのみで作成してくださいというプロンプトで作ってもらう
code:tsx
'use client'
import { useState, useEffect } from 'react'
import { Button, VStack, HStack, Text, Box } from '@chakra-ui/react'
export default function SlotMachine() {
const spin = () => {
if (coins < 10) return
setCoins(prev => prev - 10) // 1回10コイン消費
setIsSpinning(true)
// 各リールのスピン時間をずらす
spinDurations.forEach((duration, index) => {
const intervalId = setInterval(() => {
setReels(prev => {
return newReels
})
}, 100)
// スピンを停止
setTimeout(() => {
clearInterval(intervalId)
if (index === 2) {
setIsSpinning(false)
checkWin()
}
}, duration)
})
}
const checkWin = () => {
setTimeout(() => {
if (reels0 === reels1 && reels1 === reels2) { // 3つ揃いの場合
const winAmount = getWinAmount(reels0) setCoins(prev => prev + winAmount)
alert(おめでとうございます!${winAmount}コイン獲得!)
}
}, 100)
}
const getWinAmount = (symbol: string) => {
// シンボルごとの配当
'💎': 500,
'🎰': 200,
'🍇': 100,
'🍊': 50,
'🍋': 30,
'🍎': 20,
}
}
return (
<VStack spacing={6} p={6}>
<Text fontSize="2xl">残りコイン: {coins}</Text>
<Box
p={6}
borderWidth={2}
borderRadius="lg"
borderColor="gold"
bg="gray.800"
<HStack spacing={4}>
{reels.map((symbol, index) => (
<Box
key={index}
w="80px"
h="80px"
bg="white"
borderRadius="md"
display="flex"
alignItems="center"
justifyContent="center"
fontSize="40px"
{symbol}
</Box>
))}
</HStack>
</Box>
<Button
colorScheme="yellow"
size="lg"
isDisabled={isSpinning || coins < 10}
onClick={spin}
スピン (10コイン)
</Button>
<VStack spacing={2}>
<Text>配当表</Text>
{Object.entries(symbols).map((symbol) => (
<Text key={symbol}>
{symbol} x 3: {getWinAmount(symbol)}コイン
</Text>
))}
</VStack>
</VStack>
)
}
特にNext.js等の指定もなくそれっぽいコードが生まれた
chakra-uiを使ってないので書き換えてもらう
chakra-uiは使ってないのでtailwindのみ使ってください
code:tsx
'use client'
import { useState } from 'react'
export default function SlotMachine() {
const spin = () => {
if (coins < 10) return
setCoins(prev => prev - 10)
setIsSpinning(true)
spinDurations.forEach((duration, index) => {
const intervalId = setInterval(() => {
setReels(prev => {
return newReels
})
}, 100)
setTimeout(() => {
clearInterval(intervalId)
if (index === 2) {
setIsSpinning(false)
checkWin()
}
}, duration)
})
}
const checkWin = () => {
setTimeout(() => {
if (reels0 === reels1 && reels1 === reels2) { const winAmount = getWinAmount(reels0) setCoins(prev => prev + winAmount)
alert(おめでとうございます!${winAmount}コイン獲得!)
}
}, 100)
}
const getWinAmount = (symbol: string) => {
'💎': 500,
'🎰': 200,
'🍇': 100,
'🍊': 50,
'🍋': 30,
'🍎': 20,
}
}
return (
<div className="flex flex-col items-center gap-6 p-6">
<h1 className="text-2xl font-bold">
残りコイン: {coins}
</h1>
<div className="p-6 bg-gray-800 border-4 border-yellow-400 rounded-lg shadow-lg">
<div className="flex gap-4">
{reels.map((symbol, index) => (
<div
key={index}
className="w-20 h-20 bg-white rounded-md flex items-center justify-center text-4xl shadow-inner"
{symbol}
</div>
))}
</div>
</div>
<button
className={`
px-8 py-4 text-lg font-bold rounded-lg
${isSpinning || coins < 10
? 'bg-gray-400 cursor-not-allowed'
: 'bg-yellow-500 hover:bg-yellow-600 active:bg-yellow-700'}
text-white transition-colors
`}
disabled={isSpinning || coins < 10}
onClick={spin}
スピン (10コイン)
</button>
<div className="text-center">
<h2 className="font-bold mb-2">配当表</h2>
<div className="space-y-1">
{Object.entries(symbols).map((symbol) => (
<p key={symbol} className="text-sm">
{symbol} x 3: {getWinAmount(symbol)}コイン
</p>
))}
</div>
</div>
</div>
)
}
特に問題なく書き換わる
リールの判定がおかしかったので修正
リールを回す前の状態を基準に当たりが判定されているので修正してください
code:tsx
// ... existing code ...
const spin = () => {
if (coins < 10) return
setCoins(prev => prev - 10)
setIsSpinning(true)
const finalReels = '', '' // 最終的なリールの状態を保持
spinDurations.forEach((duration, index) => {
const intervalId = setInterval(() => {
setReels(prev => {
return newReels
})
}, 100)
setTimeout(() => {
clearInterval(intervalId)
// 最後のシンボルを保存
setReels(prev => {
return newReels
})
if (index === 2) {
setIsSpinning(false)
// 最終的なリールの状態で当たり判定
checkWin(finalReels)
}
}, duration)
})
}
const checkWin = (finalReels: string[]) => {
setTimeout(() => {
if (finalReels0 === finalReels1 && finalReels1 === finalReels2) { const winAmount = getWinAmount(finalReels0) setCoins(prev => prev + winAmount)
alert(おめでとうございます!${winAmount}コイン獲得!)
}
}, 100)
}
// ... existing code ...
これで出来上がったのがこれ
https://gyazo.com/70269d17a08b2d964e47425b470690a3
それっぽいのが出来た、凄い
次に文字を斬るアプリを作ってもらった
文字をクリックした軌跡で斬れるアプリを追加してくれませんか?から初めて色々修正
https://gyazo.com/6a43214ec4386d8e0bbadd4a5644655a
それっぽいが全然意図通り動かない。失敗じゃな
プロンプトで作りたいイメージを伝えるのが難しいかも
ただ破綻なくそれっぽいコードを書き上げられるのが凄い。
また、ディレクトリ構造を元に新しいコード(リンクなど)追加してくれる体験はすごい
最後に2Dの迷路ゲームを作ってもらう
このページにcanvasを使ったドット風の迷路ゲームを実装してください。Next.jsを用い、tailwindでスタイリングしてください。
https://gyazo.com/d2b0cb195c00b69c8e475872d8fc0aa1
これはかなりクオリティが高い
最初に技術スタックも指示することで一発で理想の迷路ゲームを作ってくれた
迷路はランダム生成でキーボード操作できる
このコードの説明
code:md
このコードで実装される機能は以下の通りです:
1. ランダムな迷路の自動生成
2. キーボードの矢印キーによるプレイヤー操作
3. 壁との衝突判定
4. ゴール判定
5. レスポンシブなデザイン
主な特徴:
- 深さ優先探索アルゴリズムを使用して迷路を生成
- Canvasを使用してゲームを描画
- Tailwind CSSでスタイリング
- Next.jsのクライアントサイドコンポーネントとして実装
- シンプルな操作性(矢印キーのみ)
プレイヤーは赤い四角で表示され、緑のマスがゴールとなります。壁は黒、通路は白で表示されます。「ゲームスタート」ボタンを押すと新しい迷路が生成され、ゲームを開始できます。
しっかりと要件定義して作ってくれる
少し変なアプリを作らせると出力が安定しませんが、迷路のような典型的なアプリならこのクオリティで30秒で完成するのは凄い
Copilotで似たような事をする場合、関数単位でコメントをしながら実装すれば似たような事ができるかと思う
ある程度設計が決まっている場合はかなり強力
Cursorはファイル単位でこのコード生成が出来るので凄い
脳内の設計をする前に完成するので思考を乗っ取られますが、良いプロンプトを書けば典型的な実装は全部任せられそう
テストコード?知らん
とは言ったが試してみる
ロジックとビューの責務を分けてください
code:md
はい、ロジックとビューを分離するためにファイルを分割しましょう。以下のような構造を提案します:
- MazeGame.ts - ゲームロジッククラス
- useMazeGame.ts - カスタムフック
- page.tsx - UIコンポーネント
こんな感じで分けたコードを出力してくれた
applyすればファイルも作ってくれたりくれなかったりする(多分変な場所にファイル生成されてる
あとは各ファイルでテストコードを書いてくださいと指示すれば書いてくれる
code:md
このテストコードでは以下の点をテストしています:
1. initializeMazeメソッド
- 迷路の正しいサイズ
- スタート位置の設定
- ゴール位置の設定
- 外壁の確認
2. isValidMoveメソッド
- 壁への移動の無効性
- 通路への移動の有効性
- ゴールへの移動の有効性
- 範囲外への移動の無効性
3. isGoalメソッド
- ゴール位置での判定
- 非ゴール位置での判定
4. getUnvisitedNeighborsメソッド(プライベートメソッド)
- 未訪問の隣接セルの取得
- 壁際での移動制限
AIが書いたコードを元にAIが書いたテストコード、何も品質を保証してくれないけどそれっぽいコードは出力してくれる
一旦ここまで、全体的にかなりポジティブな感触
もっと触りたい