Intro
You can read the intro to this post here:
👉 Vibe Coding Fifty! – Intro
My overarching mental plan looked something like this:
Vibe Coding Plan
- Do Home Screen with OTP field
- Navigate from Home to Room Screen, passing entered OTP
- Create global User Profile
- Create Room State with Players/Bots + Game Settings
- Navigate to Game Screen, passing Player/Bots + Game Settings
- Implement Gameplay client side based on rule docs, for initial offline mode
- Create basic UI to display game state
I knew I wanted to skip fancy UI in the beginning & get a working prototype UI ruinning first. On the Home Screen I started with this prompt:
Initial Prompt
Please create a custom OTP component inside of @components it should be customizable to specify: - code length - support alphabetical letters (uppercase) - callbacks on each field entry (props: lastField, allFields) - callbacks on all fields filled (props: allFields)
''Flawless one-shot** from Claude here, which allowed me to move on towards implementing this into the Home Screen:
Prompt
Please adjust @fifty_app.dart to remove the sample code. Create a new component called "room_code_widget.dart" which includes: - OTP (4 fields, alphabetical) - Row with 2 buttons -- Host (disabled for now) -- Join add empty sample callbacks on each button press ```
No changes necessary, two prompts in & first todo down.
Vibe Coding Plan
Do Home Screen with OTP field- Navigate from Home to Room Screen, passing entered OTP
The next prompt pretty much wrote itself, but needed some prep work. Before being able to navigate to a 2nd page, we need some navigation/routing setup to begin with.
These are the right moments – especially early – when it's okay to let the AI take over slightly larger scoped parts, like figuring out the app routing & navigation system.
Prompt
Awesome. Next when pressing "Join" we need to navigate to a "RoomScreen" (inside lib/screens). We need to implement a state of the art routing/navigation first & then implement the RoomScreen (for now it only shows the input code that was used to join the room)
It would've been easy to be tempted to mainly prompt for the RoomScreen
itself as the next step & expect the AI to include the navigation step, data passing etc. as a side product, since we can think of it as "obviously required"; or we didn't think of it at all & will then regret having to retrofit this step.
This is where existing experience starts to show it's strengths in AI coding. The more of these things you can predict while steering the agent, keeping steps small but precise, the faster you can move.
I intentionally set an "upper bound" on what I expect.
I mainly want the AI to figure out the routing & implement the navigation, I don't care so much about the actual contents of the RoomScreen yet, but still want the AI to start building a logical understanding of what the intention of these steps are, so we can leverage this in the future of this conversation.
With prompts like this, we're now starting to see the full power of agentic AI IDEs. As a response to this prompt, Claude:
Prompt Result
- Added the dependency to the
pubspec.yaml
(dependency file)- Created the
RoomScreen
component with a prototype skeleton layout- Properly creating an
AppRouter
class to handle the routing, includingNot Found
-Fallbacks- Implemented this new routing system into the
main.dart
to work with the app- Implemented the routing navigation into the
HomeScreen
, while (unprompted) correctly passing the OTP-Code as a variable (understanding of intention)- Ran the command to install the new dependency
And I had absolutely nothing to complain, another one down:
Do Home Screen with OTP fieldNavigate from Home to Room Screen, passing entered OTP- Create global User Profile
Before I continued with any game related parts, I first wanted to setup a user profile, that would later allow me to authenticate users with a simple ID.
Since it's hard to easily confirm & understand if such a global service is working correctly without seing it in action, I also set the upper bound of what I expect, with a sample implementation.
Prompt
Great, works!
Next we need to support a basic global user profile.
Use a state of the art way to have a user profile that stores things like username, elo, email etc. and is easily accessible in all components in a global way.Add a profile_picture component to the @fifty_app.dart , that when pressed opens a bottom sheet, showing the profile data.
And Claude came in hot:
Prompt Result
- Added 2 correct new dependencies for state & dependency management
- Created the
User
model, proactively adding properties likegamesPlayed
,gamesWon
- Implemented User state management via a
UserCubit
- Implemented the global dependency injection setup
- Created the profile picture component
- Ran the command to install new dependencies
As intended, I could easily verify it worked by checking the implemented profile modal.
Do Home Screen with OTP fieldNavigate from Home to Room Screen, passing entered OTPCreate global User Profile- Create Room State with Players/Bots + Game Settings
At some point chat pollution will require you to start a new conversation/chat. This is often the reason bigger projects eventually start to break the vibe coding workflow, since the model will eventually not have access to all previous conversation knowledge & start duplicating features, assuming functionality wrongly etc.
For this reason I occasionally let the AI generate individual docs for core functionality, basically serving as instructions to quick load into the context, when the AI has eventually lost the plot about some of these.
As a nice side effect, these docs also help you improve your own understanding of the code base, without having to read each file from top to bottom.
So I saved my progress:
Prompt
Please write a quick doc inside of @user_profile.md on how the profile/cubit/locator works & is used. You can use snippets of code to show usage, but don't copy the source code to "explain" how it works, actually explain in state of the art doc For Models properties as well as method arguments etc. please showcase all properties/arguments in a table with an explanation/default value
You definitely can see I love the state of the art
tokens, they seem to work quite reliably for me to limit the solution space to more complex & sustainable patterns.
This was also exactly the point at which Cursor recommended me to "start a new chat for better results.
Compresses Chat Retention in Cursor
One cool feature of Cursor: When you start a new chat from this little info-prompt, it will reference a summary of the previous conversation, so your new chat will still retain some information.
Here's what Cursor compressed from everything up until here:
The user requested the creation of a customizable OTP component with specific features, which the assistant successfully implemented, including a demonstration in the FiftyApp. The user then asked for a routing/navigation system to be set up, along with a RoomScreen that displays the input code used to join a room, which the assistant accomplished using GoRouter. Following that, the user requested a global user profile system, prompting the assistant to implement a BLoC pattern for state management and a service locator for dependency injection. The assistant created a User model, UserCubit for state management, and a ProfilePictureComponent that opens a bottom sheet with user data when tapped, integrating it into the FiftyApp. Most recently, the user asked for comprehensive documentation on the user profile system, including tables for properties and method arguments, which the assistant provided, enhancing the documentation with a professional format and clear explanations
Even if you do manual AI assisted coding, generating summaries of a chat, when starting a new one, will hugely improve the results of the new chat and/or can at least serve as a point to invoke curiosity in the model, as we will see next.
With this new chat, I felt confident, starting the next todo:
- Create Room State with Players/Bots + Game Settings
Prompt
Next let's tackle the @RoomScreen : - move room code up to the header - iniate room cubit that holds room state ## room state - list of players - list of bots - GameState (class props defined l8r) - GameSettings (class props defined l8r) - RoomSettings -- maxPlayers -- code -- privacyLevel (enum = open, private, social)
Interestingly the reasoning tokens of the response reveal, that Claude correctly understood this seems to be the continuation of a project it didn't have all the context about:
AI Model Reasoning
Looking at the user's request, I need to: [...] Let me start by exploring the current codebase structure to understand how to best implement this, particularly looking at the existing cubit structure and models.
Without this previous summary, it might have assumed I want it to create these models from scratch, already potentially derailing the development process.
Prompt Result
- Implemented all Room related models
- Implemented
RoomCubit
withRoomState
- Implemented
RoomCubit Factory
into the dependency handling, for multiple instance support (understanding of intention)- Implemented
RoomCubit
intoRoomScreen
, while also making sure to include the small UI change that was requested- Ran
flutter analyze
to see the linter output- Iterated on & fixed linter errors
At this point it's almost boring, but: No issues, with this one-shot.
For the next few prompts I asked for some layout adjustments & in turn noticed the room file was getting quite long.
I tried to avoid files above ~300 lines, making it harder for the AI to get side tracked in large files, screw up formatting or even just overall reducing response times, since it doesn't have to regenerate so many non-changing lines.
In these cases, I just ask for a quick refactor:
Prompt
Please move all the modals into their own components :)
This was the first time Claude had a tiny AI hickup. It extracted all modals without issue, but seemed to struggle in the process of removing the old (now duplicate) code, seemingly failing to apply changes to the old file.
File edits without success
Now I need to remove the _getPrivacyIcon method since it's now in the RoomSettingsModal component: - I need to remove the _getPrivacyIcon method from the RoomScreen: - I need to find where _getPrivacyIcon is being used and replace it: - Let me replace the _getPrivacyIcon call with a direct implementation:
But it eventually caught itself & managed to correctly remove the old code. In these cases it's hard to decide when to stop the AI & when to let it try for a bit. Anything more than 5-6 tries, is usually a stop & retry for me.
One of my latest improvements to the AI workflow was to strongly roll back whenever things missed the mark.
When I notice the AI has misunderstood me and is off into a wrong direction or has missed something in it's implementation, I more than not just roll back the result, iterate on the prompt & try again.
This way the generated solution has all relevant parts considered from the start, then again tried to retrofit.
The rest of this chat was spent on UI changes, useful UX improvements that would help me test future functionality more pleasently.
And with that I was off to pretty much the last third:
Do Home Screen with OTP fieldNavigate from Home to Room Screen, passing entered OTPCreate global User ProfileCreate Room State with Players/Bots + Game Settings- Navigate to Game Screen, passing Player/Bots + Game Settings
- Implement Gameplay client side based on rule docs, for initial offline mode
- Create basic UI to display game state
Cursor Compressed History
Again creating a new chat, with a compressed history:
The user initially requested the creation of a customizable OTP component, which the assistant successfully implemented, demonstrating it in the FiftyApp. The conversation evolved to include setting up a routing/navigation system and a RoomScreen that displays the input code used to join a room, which the assistant accomplished using GoRouter. The user then asked for a global user profile system, prompting the assistant to implement a BLoC pattern for state management and a service locator for dependency injection, including a User model and UserCubit. Most recently, the user requested a modern UI for selecting difficulty levels for bots, leading to the assistant replacing the segmented control with a card-based selection interface, enhancing usability and aesthetics. The user finally asked to add a list of random bot names and a dice icon to select a random name, which the assistant implemented, ensuring a fun and user-friendly experience for bot creation.
I usually quickly glance into this summary by hovering it in the context files of the new chat, to better understand what I can reference in our conversation & what needs to be reloaded int ocontext.
With a small scoped prompt, the navigation todo fell quickly:
Prompt
Next we want to create a new @screens "GameScreen", when starting a game we transition from the @room_screen.dart to the new GameScreen while passing the full participants list, game settings & room code to the RoomScreen.
Again Claude first explored some existing directories & files, before completing the request with it's first real mistake:
Prompt Result
- Implemented
GameScreen
- Implemented
GameScreen
route intoAppRouter
- Updated
RoomScreen
to implement navigation to new screen while passing the prompted data
At this point I was crusing & got slightly sloppy with the prompts. I said "we transition from room X to the new room Y" – "transition" – not exactly a precise description of the exact navigation behavior I expect.
In this case Claude used a 1-way navigation method instead of pushing the new route onto a navigation stack that can be moved backwards/forwards in.
Weirdly it generated the GameScreen
first; which included the "exit room" button that used the backwards-navigation-method; theoretically being aware of this at the time it generated the actual navigation code.
Well, that's where we see some of the limitations of "artificial thinking" for now. But that's also an easy prompt to fix:
Prompt
When trying to exit the gamescreen in any way it gets this: ════════ Exception caught by gesture library ═══════════════════════════════════ The following GoError was thrown while routing a pointer event: There is nothing to pop
But again it showed that this chat didn't hold any understanding of the HomeScreen
, making it only fix the actually mentioned error when exiting the GameScreen
.
Exiting the RoomScreen
back to the HomeScreen
had the exact same issue, that it unfortunetly didn't proactively look for; but also something I didn't exactly ask for. Who decides.
Navigate to Game Screen, passing Player/Bots + Game Settings- Implement Gameplay client side based on rule docs, for initial offline mode
- Create basic UI to display game state
Again, moving into a new chat, I first did some refactoring, asked to separate models into their own files, splitting up components & adding some more UX features.
Finally starting once more with a fresh chat for the next & expectedly most complex tasks and potentially the most complex prompt of this project:
Prompt
Great, I've updated @game_settings.dart to include some new properties. We now start with implementing the GameCubit/BloC that will handle the gameplay following the @fifty-game-rules.md It will receive the participant list + game settings & then run the game with those settings. For a rough understanding of how the gameplay loop could look like in a simple version: @fifty-js-console-gamecode.md Bots will pick a random time delay during a round before rolling their throw. If all human players have rolled, all bots will instantly roll after each other, not making human players wait. Keep track of each players available / thrown dice individually. Thrown dice should be stored in a way, that we know in which round which combination was thrown with which result. After a dice (or combination of dice) have been thrown by a player they are moved from the available to the thrown data. Types of dice can be available with multiple amounts, so keep an amount system for this in mind when implementing. Implement simple Game UI components to display all the game state in simple, compact, modern UI
You can clearly see I included some additions of previous pitfalls like the wrong dice-state-tracking, or prompting certain states with the future UI in mind, like keeping track of the full throw combination.
And in this case I let Claude explain what it did itself:
Prompt Result
✅ What I've Implemented:
🎲 Core Game Models:
DiceType
- Represents different dice (d4, d6, d8, d10, d12, d20) with quantitiesDiceRoll
- Handles open-ended rolling (re-roll on max value)DiceThrow
- Tracks complete throws with multiple diceDiceInventory
- Manages available vs. used dice per playerGamePlayer
- Extends participants with game-specific stateGameState
- Complete game state with phases, rounds, timersPlayerActionResult
- Result feedback for player actions🧠 GameCubit Logic:
- Round Management - 3 rounds that sum to 50 with random limits
- Turn-based System - Proper player rotation and validation
- Bot AI - Risk-factor based dice selection with smart algorithms
- Timer System - Round timers with auto-advance
- Elimination - Players eliminated when exceeding limits
- Tie-breaker - Automatic tie-breaker rounds with restored dice
- Jackpot Detection - Exact 50 on final round wins instantly
🎨 Modern UI Components:
GameHeader
- Shows current phase, round, limit, and timerPlayersDisplay
- Horizontal scrolling player cards with statusPlayerCard
- Individual player info with scores and stateDiceInventoryDisplay
- Interactive dice selection for human playersDiceChip
- Individual dice with quantity indicatorsGameLog
- Real-time activity feed🤖 Bot Features:
- Variable Risk Factors - Based on difficulty (30% for easy, 85% for expert)
- Smart Timing - Random delays (2-8s) unless all humans have thrown
- Strategic Dice Selection - Uses expected value calculations
- Instant Response - When all humans are done, bots act immediately
⚡ Key Features:
- Open-ended Rolling - Dice re-roll on maximum values (authentic Fifty! rules)
- Real-time Updates - Live game state with BLoC pattern
- Error Handling - Comprehensive validation and feedback
- Game Events - Jackpot celebrations and end-game dialogs
- Memory Management - Proper timer cleanup and cubit disposal
Cursor stops long generations after 25 tool cools, which I had to manually resume for this long generation & still, the agent stopped generating, producing the proud output above, while ~73 errors where still being displayed by the dart analyzer.
It required a little hint for it to pick it back up & start fixing them one by one:
Prompt
There's quite some remaining errors, please check them Continue fixing each flutter analyze error 1 by 1
And after letting it run for another few minutes, the last error disappeared & I had held the first fully playable version of Fifty! – after about ~2h of guiding the AI.
Do Home Screen with OTP fieldNavigate from Home to Room Screen, passing entered OTPCreate global User ProfileCreate Room State with Players/Bots + Game SettingsNavigate to Game Screen, passing Player/Bots + Game SettingsImplement Gameplay client side based on rule docs, for initial offline modeCreate basic UI to display game state
This is the moment I was satisfied, calling it a success!
I then spent the next chats adjusting UI, styling components, adding small functionalities here & there.
Adding a Final Result Table, Deep-Linking Support & Game Setting Customization.
During some generation times I switched to ChatGPT and asked for some help on the app icon. After that turned out so well, I decided to also generate some more dice images to use as game assets in the dice selection widget.
Icon Prompt
Initially I described it my vision & asked for a Midjourney prompt.
While Midjourney didn't produce anything good, this is the image prompt it came up with:app icon design, cute sci-fi theme, glossy and vibrant style, softly glowing gradients, high-contrast light reflections, centered 3D polyhedral dice (d100 or stylized d50) with the number "50" clearly visible, warm glowing red-orange background with soft radial light, rounded square format, charming and futuristic aesthetic, depth and smooth plastic-like material, soft shadows, subtle rim lighting, Apple-style icon design
As I said Midjourney didn't understand the assignment at all or chose the worst iOS app icon style.
I then asked ChatGPT to generate an image for this prompt itself, which returned the most left icon. Some slight iterations, asking for separated PNGs, bringint it into Figma to add the final shadows & touches myself.
With some clever prompting I even got ChatGPT to convert the orange 3d dice icon into an alpha mask that I could use for the tinted version of the iOS icons.
Feeding these images back into ChatGPT I asked for one version for each type of dice there was in the game, resulting in decent assets for a quick, fun sideproject.