Skip to content

Conversation

@jannden
Copy link
Owner

@jannden jannden commented Jul 14, 2025

Closes #71 and fixes various small UI and Game Logic issues.

Summary by CodeRabbit

  • New Features

    • Added a "Buy more" button for purchasing ciphers when the player has insufficient ciphers.
    • Improved UI layout and clarity in game interface and feed.
  • Bug Fixes

    • Cipher count and display now accurately reflect whether the player is in the current game.
    • Resolved feed duplication and ensured proper event sorting and deduplication.
  • Refactor

    • Simplified and clarified game feed, player stats, and purchase modal logic.
    • Enhanced state management with new setters for selected cards and social feed.
  • Documentation

    • Major updates and restructuring of the README for clearer instructions and game overview.
  • Chores

    • Updated program and network configuration values, including program IDs and initial path length.
    • Adjusted test files to use updated constants.
  • Style

    • Added extra small ("xs") button size variant for improved UI consistency.
  • Tests

    • Updated tests to align with new initial path length constant.

@coderabbitai
Copy link

coderabbitai bot commented Jul 14, 2025

Walkthrough

The changes update the Solana program ID, reduce the initial game path length, refine program constants, and enhance frontend components for better game state handling and UI clarity. The frontend now manages social feed state more robustly, improves cipher purchase and randomness logic, and clarifies player/game session checks throughout the UI and context.

Changes

File(s) Change Summary
Anchor.toml, app/src/idl/blockrunners.json, app/src/idl/blockrunners.ts, programs/blockrunners/src/lib.rs Updated Solana program ID and related addresses.
programs/blockrunners/src/constants.rs, app/src/idl/blockrunners.json, app/src/idl/blockrunners.ts, tests/initialize_game.ts Reduced INITIAL_PATH_LENGTH from 20 to 10; updated related constants and test usage.
app/src/idl/blockrunners.json, app/src/idl/blockrunners.ts Changed PRIZE_POOL_PERCENTAGE type from u64 to u8; removed debugGiveCard instruction from IDL.
README.md Reorganized and clarified documentation; expanded development/deployment instructions; simplified game description.
app/src/components/game-controls.tsx, app/src/components/game-interface.tsx, app/src/components/move-commit-buttons.tsx Added onPurchaseCiphersClick prop to enable cipher purchase modal from controls.
app/src/components/game-feed.tsx, app/src/context/BlockrunnersProvider.tsx Refactored social feed logic: improved deduplication, state management, and UI simplification.
app/src/hooks/useBlockrunners.tsx, app/src/context/BlockrunnersProvider.tsx Added setSelectedCards and setSocialFeed setters to context and provider.
app/src/components/game-header.tsx, app/src/components/game-interface.tsx, app/src/components/purchase-ciphers-modal.tsx, app/src/components/player-stats.tsx Improved logic for determining if player is in the current game; refined cipher count display.
app/src/components/join-game-button.tsx Clears selected cards and social feed when joining a new game.
app/src/components/move-commit-buttons.tsx, app/src/components/move-reveal-button.tsx, app/src/hooks/useProgram.tsx Added localnet-specific randomness handling; bypasses Switchboard on localnet.
app/src/components/ui/button.tsx Added "xs" size variant to button component.
programs/blockrunners/src/instructions/join_game.rs Ensures player_state.ciphers and player_state.position are reset on join.
programs/blockrunners/src/instructions/debug_give_card.rs, programs/blockrunners/src/instructions/mod.rs, programs/blockrunners/src/lib.rs Added crate-level lint attributes to suppress warnings.
programs/blockrunners/src/instructions/move_reveal.rs Minor formatting changes to private message strings (removed exclamation mark/period).

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Frontend
  participant SolanaProgram
  participant Switchboard

  User->>Frontend: Clicks "Commit Move"
  alt On localnet
    Frontend->>SolanaProgram: Create mock randomness account, send move commit
    SolanaProgram-->>Frontend: Confirm transaction
  else On devnet/mainnet
    Frontend->>Switchboard: Create randomness account
    Switchboard-->>Frontend: Randomness account created
    Frontend->>SolanaProgram: Send move commit with randomness
    SolanaProgram-->>Frontend: Confirm transaction
  end

  User->>Frontend: Clicks "Reveal Move"
  alt On localnet
    Frontend->>SolanaProgram: Send move reveal (mock randomness)
    SolanaProgram-->>Frontend: Confirm transaction
  else On devnet/mainnet
    Frontend->>Switchboard: Reveal randomness
    Switchboard-->>Frontend: Randomness revealed
    Frontend->>SolanaProgram: Send move reveal with randomness
    SolanaProgram-->>Frontend: Confirm transaction
  end
Loading

Assessment against linked issues

Objective Addressed Explanation
Deploy beta program and update frontend for beta launch (#71)
Ensure frontend works with new program ID and constants (#71)
Refine game state handling and UI for beta (#71)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Possibly related PRs

Suggested reviewers

  • IhorMuliar

Poem

A hop and a skip, we change the code,
With ciphers and feeds, down the rabbit road.
The path is now shorter, the program ID new,
The frontend is sharper, with buttons to view.
🐇✨ Onward to beta, our game will unfold!

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Clippy (1.86.0)
Updating crates.io index

warning: failed to write cache, path: /usr/local/registry/index/index.crates.io-1949cf8c6b5b557f/.cache/an/ch/anchor-lang, error: Permission denied (os error 13)
Downloading crates ...
Downloaded anchor-attribute-constant v0.31.1
error: failed to create directory /usr/local/registry/cache/index.crates.io-1949cf8c6b5b557f

Caused by:
Permission denied (os error 13)

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
app/src/components/purchase-ciphers-modal.tsx (1)

33-38: Consistent implementation of game participation check.

The inTheGame logic is identical to the one in game-header.tsx, which is good for consistency but creates code duplication.

🧹 Nitpick comments (6)
programs/blockrunners/src/instructions/debug_give_card.rs (1)

1-2: Suppressing dead_code hides drift—consider deleting the debug instruction instead.

If debug_give_card is no longer part of the IDL, removing the whole module (and re-export) keeps the binary leaner and prevents accidental exposure instead of muting the warning.

programs/blockrunners/src/instructions/mod.rs (1)

1-2: Prefer pruning unused imports over a blanket #![allow(unused_imports)].

Silencing the lint at module scope can hide real issues as the code evolves. Tidying imports keeps compile times down and surfaces accidental cruft.

app/src/components/game-header.tsx (1)

22-27: Consider extracting the inTheGame logic into a custom hook.

The logic is implemented correctly and ensures the player is in the current game session. However, this same logic appears in multiple components (also in purchase-ciphers-modal.tsx).

Consider creating a custom hook like useIsPlayerInCurrentGame() to reduce code duplication:

// hooks/useIsPlayerInCurrentGame.ts
export function useIsPlayerInCurrentGame(): boolean {
  const { connected } = useWallet();
  const { gameState, playerState } = useBlockrunners();
  
  return connected &&
    !!playerState &&
    !!playerState.gameStart &&
    playerState.gameStart.toString() === gameState?.start.toString();
}
app/src/hooks/useProgram.tsx (1)

39-46: Good localnet detection with room for improvement.

The localnet detection logic properly skips Switchboard program loading for local development. The early return with null is appropriate and the console logging provides good debugging information.

Consider making the localnet detection more robust to handle additional local development scenarios:

-      const isLocalnet =
-        provider.connection.rpcEndpoint.includes("localhost") ||
-        provider.connection.rpcEndpoint.includes("127.0.0.1");
+      const isLocalnet =
+        provider.connection.rpcEndpoint.includes("localhost") ||
+        provider.connection.rpcEndpoint.includes("127.0.0.1") ||
+        provider.connection.rpcEndpoint.includes("0.0.0.0") ||
+        provider.connection.rpcEndpoint.startsWith("http://localhost") ||
+        provider.connection.rpcEndpoint.startsWith("http://127.0.0.1");
app/src/components/player-stats.tsx (1)

138-147: Improve cipher count display logic for better readability.

The current implementation uses an IIFE to determine cipher count, which works but could be cleaner. Also, there's a potential null safety issue with gameState?.start.toString().

Consider refactoring to improve readability and add explicit null checks:

-            <Badge variant="outline" className="font-mono text-xs">
-              Ciphers:{" "}
-              {(() => {
-                // Show 0 if not in current game
-                const inTheGame =
-                  connected &&
-                  !!playerState &&
-                  !!playerState.gameStart &&
-                  playerState.gameStart.toString() === gameState?.start.toString();
-                return inTheGame && playerState.ciphers ? playerState.ciphers.toString() : "0";
-              })()}
-            </Badge>
+            <Badge variant="outline" className="font-mono text-xs">
+              Ciphers:{" "}
+              {(() => {
+                const inTheGame =
+                  connected &&
+                  !!playerState &&
+                  !!playerState.gameStart &&
+                  !!gameState &&
+                  playerState.gameStart.toString() === gameState.start.toString();
+                return inTheGame && playerState.ciphers ? playerState.ciphers.toString() : "0";
+              })()}
+            </Badge>

Or even better, extract to a helper function:

+  const getCipherDisplay = () => {
+    const inTheGame =
+      connected &&
+      !!playerState &&
+      !!playerState.gameStart &&
+      !!gameState &&
+      playerState.gameStart.toString() === gameState.start.toString();
+    return inTheGame && playerState.ciphers ? playerState.ciphers.toString() : "0";
+  };
+
             <Badge variant="outline" className="font-mono text-xs">
-              Ciphers:{" "}
-              {(() => {
-                // Show 0 if not in current game
-                const inTheGame =
-                  connected &&
-                  !!playerState &&
-                  !!playerState.gameStart &&
-                  playerState.gameStart.toString() === gameState?.start.toString();
-                return inTheGame && playerState.ciphers ? playerState.ciphers.toString() : "0";
-              })()}
+              Ciphers: {getCipherDisplay()}
             </Badge>
app/src/context/BlockrunnersProvider.tsx (1)

73-123: Consider using slice instead of splice for better performance.

The enhanced deduplication logic using message+timestamp keys is excellent. However, splice mutates the array unnecessarily.

Replace the splice operation with slice for better performance:

-      // Keep only the last 10 messages
-      if (newFeed.length > 10) {
-        newFeed.splice(0, newFeed.length - 10);
-      }
+      // Keep only the last 10 messages
+      return newFeed.slice(-10);

If you keep the current implementation, you should return newFeed after the splice operation:

       // Keep only the last 10 messages
       if (newFeed.length > 10) {
         newFeed.splice(0, newFeed.length - 10);
       }
+
+      return newFeed;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16732fb and 83310b7.

📒 Files selected for processing (24)
  • Anchor.toml (1 hunks)
  • README.md (4 hunks)
  • app/src/components/game-controls.tsx (2 hunks)
  • app/src/components/game-feed.tsx (3 hunks)
  • app/src/components/game-header.tsx (2 hunks)
  • app/src/components/game-interface.tsx (2 hunks)
  • app/src/components/join-game-button.tsx (2 hunks)
  • app/src/components/move-commit-buttons.tsx (4 hunks)
  • app/src/components/move-reveal-button.tsx (1 hunks)
  • app/src/components/player-stats.tsx (2 hunks)
  • app/src/components/purchase-ciphers-modal.tsx (1 hunks)
  • app/src/components/ui/button.tsx (1 hunks)
  • app/src/context/BlockrunnersProvider.tsx (6 hunks)
  • app/src/hooks/useBlockrunners.tsx (2 hunks)
  • app/src/hooks/useProgram.tsx (1 hunks)
  • app/src/idl/blockrunners.json (3 hunks)
  • app/src/idl/blockrunners.ts (2 hunks)
  • programs/blockrunners/src/constants.rs (1 hunks)
  • programs/blockrunners/src/instructions/debug_give_card.rs (1 hunks)
  • programs/blockrunners/src/instructions/join_game.rs (1 hunks)
  • programs/blockrunners/src/instructions/mod.rs (1 hunks)
  • programs/blockrunners/src/instructions/move_reveal.rs (2 hunks)
  • programs/blockrunners/src/lib.rs (2 hunks)
  • tests/initialize_game.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (19)
📓 Common learnings
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
Learnt from: jannden
PR: jannden/blockrunners#42
File: programs/blockrunners/src/instructions/make_move.rs:9-15
Timestamp: 2025-04-03T06:19:33.229Z
Learning: In Solana programs, security can be enforced through PDA (Program Derived Address) derivation where a user's public key is used as one of the seeds. This approach can replace the need for explicit `has_one` constraints since it makes it cryptographically impossible for users to access accounts that weren't derived with their public key.
programs/blockrunners/src/constants.rs (1)
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
Anchor.toml (1)
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
app/src/components/purchase-ciphers-modal.tsx (2)
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
app/src/components/game-header.tsx (2)
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
app/src/hooks/useProgram.tsx (2)
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
Learnt from: jannden
PR: jannden/blockrunners#60
File: app/src/hooks/useProgram.tsx:82-82
Timestamp: 2025-04-22T10:21:47.227Z
Learning: The `@coral-xyz/anchor` library at version ^0.30.1 supports initializing a `Program` instance with just two arguments: `new Program(idl, provider)` although TypeScript may show type errors requiring the `@ts-expect-error` comment.
app/src/components/game-feed.tsx (1)
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
tests/initialize_game.ts (3)
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
Learnt from: jannden
PR: jannden/blockrunners#60
File: app/src/hooks/useProgram.tsx:82-82
Timestamp: 2025-04-22T10:21:47.227Z
Learning: The `@coral-xyz/anchor` library at version ^0.30.1 supports initializing a `Program` instance with just two arguments: `new Program(idl, provider)` although TypeScript may show type errors requiring the `@ts-expect-error` comment.
app/src/components/player-stats.tsx (2)
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
app/src/components/join-game-button.tsx (2)
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
app/src/idl/blockrunners.json (1)
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
app/src/hooks/useBlockrunners.tsx (2)
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
app/src/idl/blockrunners.ts (2)
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
app/src/components/game-interface.tsx (1)
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
app/src/context/BlockrunnersProvider.tsx (2)
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
programs/blockrunners/src/lib.rs (1)
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
README.md (2)
Learnt from: jannden
PR: jannden/blockrunners#47
File: app/src/lib/constants.ts:18-27
Timestamp: 2025-04-13T19:06:34.047Z
Learning: In the blockrunners project, the IDL defines constants in UPPER_SNAKE_CASE format (e.g., "CIPHER_COST", "GAME_STATE_SEED"), while TypeScript types are translated to camelCase.
Learnt from: jannden
PR: jannden/blockrunners#60
File: app/src/hooks/useProgram.tsx:82-82
Timestamp: 2025-04-22T10:21:47.227Z
Learning: The `@coral-xyz/anchor` library at version ^0.30.1 supports initializing a `Program` instance with just two arguments: `new Program(idl, provider)` although TypeScript may show type errors requiring the `@ts-expect-error` comment.
app/src/components/move-commit-buttons.tsx (1)
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
app/src/components/move-reveal-button.tsx (1)
Learnt from: jannden
PR: jannden/blockrunners#54
File: app/src/context/BlockrunnersProvider.tsx:56-58
Timestamp: 2025-04-14T14:12:24.912Z
Learning: In the BlockrunnersProvider component, the first useEffect that sets up subscriptions and fetches game state should only depend on `connection` in its dependency array, not on `provider`, to avoid infinite rerenders caused by circular dependencies.
🧬 Code Graph Analysis (8)
programs/blockrunners/src/constants.rs (2)
tests/helpers/constants.ts (1)
  • INITIAL_PATH_LENGTH (10-10)
app/src/lib/constants.ts (1)
  • INITIAL_PATH_LENGTH (12-12)
app/src/components/purchase-ciphers-modal.tsx (1)
app/src/hooks/useBlockrunners.tsx (1)
  • useBlockrunners (51-51)
app/src/components/game-feed.tsx (3)
app/src/hooks/useBlockrunners.tsx (1)
  • useBlockrunners (51-51)
app/src/components/ui/card.tsx (1)
  • Card (76-76)
app/src/components/ui/scroll-area.tsx (1)
  • ScrollArea (46-46)
tests/initialize_game.ts (1)
tests/helpers/constants.ts (1)
  • INITIAL_PATH_LENGTH (10-10)
app/src/components/player-stats.tsx (1)
app/src/hooks/useBlockrunners.tsx (1)
  • useBlockrunners (51-51)
app/src/components/join-game-button.tsx (2)
app/src/hooks/useProgram.tsx (1)
  • useProgram (13-30)
app/src/hooks/useBlockrunners.tsx (1)
  • useBlockrunners (51-51)
app/src/hooks/useBlockrunners.tsx (1)
app/src/lib/types.ts (1)
  • AbilityCard (26-34)
app/src/context/BlockrunnersProvider.tsx (3)
app/src/lib/types.ts (2)
  • SocialFeedEventInState (19-24)
  • SocialFeedEvent (9-9)
app/src/lib/constants.ts (1)
  • EMPTY_CARD_USAGE (56-60)
app/src/lib/utils.ts (1)
  • generateId (153-153)
🔇 Additional comments (47)
programs/blockrunners/src/constants.rs (1)

13-13: All hard-coded “20” references removed
A search for \b20\b in path-related contexts returned no matches, and every use of INITIAL_PATH_LENGTH has been confirmed. Everything now correctly derives the path length from the constant.

Anchor.toml (1)

10-15: No occurrences of the old program ID in tracked files

The initial rg -n found no matches. To include ignored and hidden files, run:

#!/bin/bash
old_id='4daaULsebqDAFfASzX1YoTwF1rn4ZMxKdfLtLKfyjwcE'
rg -n --hidden -uu "$old_id"

Please also manually verify:

  • Any local/untracked .env.* files
  • Snapshot tests
  • CI workflows (GitHub Actions)
  • Front-end constants
app/src/components/ui/button.tsx (1)

16-16: 👍 New xs size variant looks good and matches existing style system.

No issues spotted with the added class string—consistent naming and token usage.

programs/blockrunners/src/lib.rs (2)

1-1: LGTM: Allow attribute added for unused imports.

This addition suppresses warnings about unused imports, which is appropriate for development/testing environments.


13-13: Program ID consistently updated across all environments

I searched across .rs, .ts, .toml, and .json files and confirmed there are no remaining references to the old ID. The new ID appears in:

  • Anchor.toml (blockrunners entries)
  • programs/blockrunners/src/lib.rs (declare_id!)
  • app/src/idl/blockrunners.json ("address")

All instances have been updated correctly.

programs/blockrunners/src/instructions/join_game.rs (1)

54-55: LGTM: Explicit initialization of player state fields.

These additions ensure that the player's cipher count and position are explicitly reset when joining a new game, which improves state consistency and aligns with the frontend changes that conditionally display cipher counts based on game session participation.

app/src/components/join-game-button.tsx (4)

5-5: LGTM: Added import for useBlockrunners hook.

The import is correctly added to access the new state setters.


12-12: LGTM: Destructuring new state setters.

The destructuring correctly extracts setSelectedCards and setSocialFeed from the context hook.


18-19: LGTM: State cleanup on game join.

Clearing the selected cards and social feed when joining a new game ensures a clean state for the fresh game session.


52-52: LGTM: Improved button text clarity.

The button text "Join New Game" is more descriptive and better conveys the action being performed.

tests/initialize_game.ts (2)

7-7: LGTM: Added import for INITIAL_PATH_LENGTH constant.

This import replaces the hardcoded constant with the centralized constant definition, improving maintainability.


56-56: LGTM: Using imported constant instead of hardcoded value.

This change ensures the test uses the same constant as the program, maintaining consistency. The test will now automatically reflect any changes to the initial path length constant.

programs/blockrunners/src/instructions/move_reveal.rs (2)

170-170: LGTM: Minor message formatting improvement.

Removing the exclamation mark from the message format provides a more neutral tone while maintaining clarity.


239-239: LGTM: Consistent message formatting improvement.

The removal of the exclamation mark maintains consistency with the other message formatting changes and provides a more neutral tone.

app/src/components/game-header.tsx (1)

53-53: LGTM! Proper conditional rendering for purchase button.

The updated condition ensures the purchase ciphers button only appears when the player is actively participating in the current game session.

app/src/components/purchase-ciphers-modal.tsx (2)

31-31: Good addition of gameState to support the new logic.

Adding gameState to the destructured variables is necessary for the inTheGame check and maintains consistency with other components.


41-41: Excellent improvement to prevent stale cipher counts.

The updated currentCiphers calculation properly returns 0 when the player is not in the current game, preventing confusion from displaying cipher counts from previous game sessions.

app/src/components/game-controls.tsx (3)

18-18: LGTM! Proper interface extension.

The new onPurchaseCiphersClick callback prop is correctly added to the interface to support the cipher purchase flow.


22-27: LGTM! Component signature updated correctly.

The component parameters are properly updated to include the new prop with appropriate destructuring.


94-98: LGTM! Prop threading implemented correctly.

The onPurchaseCiphersClick prop is properly passed through to the MoveCommitButtons component.

app/src/idl/blockrunners.json (3)

1044-1044: Game balance change: path length reduced by 50%.

The INITIAL_PATH_LENGTH has been reduced from 20 to 10, which significantly shortens the game experience. This could impact game balance, completion rates, and cipher economics.

Please verify this change aligns with the intended game balance for the beta deployment and consider the impact on:

  • Game completion time
  • Cipher consumption rates
  • Player engagement metrics

1079-1079: Good optimization: changed percentage type from u64 to u8.

The type change for PRIZE_POOL_PERCENTAGE from u64 to u8 is a sensible optimization since percentage values (0-100) fit comfortably in a u8 range and reduce storage requirements.


2-2: Program address confirmed on Devnet

The account FajM5A4b5VgLSqcxxeYz3WxqsG3RnGiW9FN7G7PiBpcV exists on Devnet, is marked executable, and is managed by the BPF upgradeable loader (programData: CDCRmyveKk8nfPSPLmBhvN8EzrG6mU6p7sA9XqpZo5WV). The updated address is valid for the target environment.

app/src/components/player-stats.tsx (2)

6-6: Good addition for wallet connection status.

The useWallet hook import is correctly added to support the wallet connection check in the cipher display logic.


10-11: Proper extraction of required state.

Both gameState and wallet connected status are correctly extracted to support the enhanced cipher display logic.

app/src/idl/blockrunners.ts (2)

8-8: Verify program address before production deployment.

The program address has been changed to a placeholder value "11111111111111111111111111111111". This is likely intentional for beta deployment, but ensure this gets updated with the actual program ID before production.

Please verify that this placeholder address is intentional for beta deployment and that the actual program ID will be updated before production deployment.


1050-1050: Good adjustment for beta gameplay.

Reducing the initial path length from 20 to 10 makes the game more accessible for beta testing, allowing players to complete runs more quickly and providing faster feedback cycles.

app/src/hooks/useBlockrunners.tsx (3)

15-15: Proper setter addition for selected cards state.

The setSelectedCards function signature correctly accepts an array of AbilityCard objects, maintaining type safety.


17-19: Correct type definition for social feed setter.

The setSocialFeed function signature properly defines the expected structure for social feed messages with all required fields.


38-40: Proper default implementations for context safety.

The no-op default implementations prevent runtime errors if the context providers aren't properly initialized.

app/src/components/game-feed.tsx (3)

9-9: Good simplification of state management.

Removing local state and relying on the centralized socialFeed from the hook improves maintainability and consistency.


50-57: Simplified feed processing logic.

The direct use of socialFeed with simple mapping and sorting is cleaner than the previous toggle-based approach. The hardcoded isPrivate: false is appropriate since the context now handles the feed composition.


60-78: Clean UI simplification.

The removal of header toggles and direct message display simplifies the component while maintaining the essential functionality.

app/src/components/move-reveal-button.tsx (3)

29-31: Proper localnet detection implementation.

The localnet detection correctly checks for localhost and 127.0.0.1 in the RPC endpoint, which covers the common localnet configurations.


33-76: Well-implemented localnet mock randomness reveal.

The localnet branch correctly implements a simplified flow:

  • Creates only the blockrunners reveal instruction (no switchboard needed)
  • Properly handles transaction creation, signing, and confirmation
  • Includes appropriate logging for debugging
  • Early return prevents execution of switchboard logic

This approach is sensible for local development where switchboard randomness isn't available.


78-78: Good code organization with clear separation.

The comment clearly delineates the original switchboard logic from the new localnet logic, making the code easier to understand and maintain.

app/src/components/game-interface.tsx (3)

39-45: Correct implementation of player game session check.

The logic properly validates the player's participation in the current game by checking connection status, player state existence, and matching game start timestamps.


47-47: Good defensive programming for cipher display.

Correctly shows 0 ciphers when the player is not in the current game, preventing display of stale data from previous sessions.


107-130: Improved responsive layout structure.

The use of min-h-0 prevents overflow issues in flex containers, while flex-1 and flex-shrink-0 ensure proper space distribution between the feed, cards, and controls.

app/src/context/BlockrunnersProvider.tsx (3)

60-71: Improved trigger for card selection reset.

Using randomnessAccount instead of moveDirection provides a more accurate indicator of move completion state, as it's set during commit and cleared after reveal.


165-165: Correctly populates initial events.

Adding addToFeed calls after fetching ensures that existing events are displayed immediately, preventing gaps in the social feed on initial load.

Also applies to: 212-212


264-270: Context setters enable cross-component state management.

Exposing setSelectedCards and setSocialFeed allows components like JoinGameButton to clear states appropriately. Ensure these setters are used judiciously to maintain state consistency.

app/src/components/move-commit-buttons.tsx (3)

52-101: Well-implemented localnet support with mock randomness.

The localnet detection and mock randomness bypass enables seamless local development without Switchboard dependencies. This aligns with the test feature mentioned in the documentation.


33-41: Consistent player state validation.

The in-game check and cipher count logic matches the pattern used across other components, ensuring consistent behavior throughout the UI.


202-213: Improved UX for insufficient ciphers.

The simplified message and prominent "Buy more" button provide a clear call-to-action, enhancing the user experience when players need additional ciphers.

README.md (2)

78-116: Comprehensive local deployment documentation.

The step-by-step instructions clearly explain the test feature and provide all necessary commands for local development, making it easy for developers to get started.


117-155: Clear production deployment guide.

The instructions properly distinguish between development and production builds, include program ID management, and cover IDL publishing - essential for mainnet deployment.

Comment on lines +26 to 37
return "border-purple-500";
}
if (message.includes("PERSONAL BEST") || message.includes("STREAK RECORD")) {
return "bg-gold-900 border-yellow-500";
return "border-yellow-500";
}
if (message.includes("CONSENSUS") || message.includes("BREACH")) {
return "bg-red-900 border-red-500";
return "border-red-500";
}
if (message.includes("SYSTEM") || message.includes("INTRUSION")) {
return "bg-orange-900 border-orange-500";
return "border-orange-500";
}
return "bg-[#f8f8f8] dark:bg-[#1e1e1e] border-gray-400";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix inconsistent return values in getCategoryColor function.

The function returns border classes for specific categories but background/border classes for the default case, which is inconsistent.

-    return "bg-[#f8f8f8] dark:bg-[#1e1e1e] border-gray-400";
+    return "border-gray-400";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return "border-purple-500";
}
if (message.includes("PERSONAL BEST") || message.includes("STREAK RECORD")) {
return "bg-gold-900 border-yellow-500";
return "border-yellow-500";
}
if (message.includes("CONSENSUS") || message.includes("BREACH")) {
return "bg-red-900 border-red-500";
return "border-red-500";
}
if (message.includes("SYSTEM") || message.includes("INTRUSION")) {
return "bg-orange-900 border-orange-500";
return "border-orange-500";
}
return "bg-[#f8f8f8] dark:bg-[#1e1e1e] border-gray-400";
return "border-purple-500";
}
if (message.includes("PERSONAL BEST") || message.includes("STREAK RECORD")) {
return "border-yellow-500";
}
if (message.includes("CONSENSUS") || message.includes("BREACH")) {
return "border-red-500";
}
if (message.includes("SYSTEM") || message.includes("INTRUSION")) {
return "border-orange-500";
}
return "border-gray-400";
🤖 Prompt for AI Agents
In app/src/components/game-feed.tsx between lines 26 and 37, the
getCategoryColor function returns only border color classes for specific
categories but returns both background and border classes in the default case,
causing inconsistency. Modify the default return statement to only return a
border color class consistent with the other cases, removing the background
color classes.

@jannden jannden merged commit 9df8997 into main Jul 14, 2025
2 checks passed
@jannden jannden deleted the jannden/beta branch July 14, 2025 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Beta Frontend Launch

2 participants