Skip to content

itsantonle/LongDrome_Game

Repository files navigation

📱About LongDrome

Next.js TypeScript Tailwind CSS shadcn/ui Framer Motion

Version 1.0 - Final Project For Algorithms - SE2231 - Developed by A. Legayada

Face Upon an Enemy that Cannot Be Killed! You can only Defend yourself and Negotiate!

This project runs on NextJS and is a browser based game that relies on ShadCN and TailwindCSS. You're facing against an immortal entity that cannot be killed, it has secrets and you need to understand them. Go through color blocks and defend yourself against a foe who can hit YOU, but you CANNOT hit it. A turn based combat that focuses on 'talking' and pattern identification. Can you prove how good you are at the patterns?

⚙️ Releases:

May 2025 - untested_release_v.1.0

May 2025 (latest) untested_release_v1.0.1

🖥 Live Deployments:

Vercel - https://long-drome-game.vercel.app/


Running the development build

✅ Clone this repository bash git clone https://github.com/itsantonle/LongDrome_Game.git

✅ Install dependencies bash npm install

✅ Run the developement bash npm run dev

📔Table of Contents

📖 Algorithm Design

🎮 Playing the Game

🤝 Contribution

📜 License



🕹️Features

🎲Palindrome Color Selection

Select colors to defend yourself! Each turn you must choose the longest Palindrome or you will take damage!

image

🖼️Different Locations

Different Locations that feature different backdrop and art. You can choose to fight in the temple or go home and recover!

image

❤️‍🩹Amiabliity Bar

Improve your relationship with the Guardian by talking and and choosing the correct responses!

image

🖱️Intearctive Action Buttons

Use the buttons to navigate and continue with the game! Talk, fight and use Magic!

image

🧑‍🎨Responsive Design

Playing on Mobile or on Laptop screens? No problem.

longdrome responseive

☀️Light / Dark Mode with Next/Themes

Play in Dark or Light Mode

💡Utilization of Manacher's Algorithm

The Game Mechanics use the famous Manacher Algorithm that finds the longest palindromic strings in linear O(n) time!

this sections handles the process behind the functions
1. setOptimal palindrome gets from determineOptimalPalindrome(client_helper_utils) - calls from main page, validates palindrome
2. findOptimalPalindrome(game_utils) - error handling and returns sequence to determineOptimalPalindrome, validates manacher, otherwise brute force
3. findLongestPalindromeManachger(palindrome_utils) - base case (probably will never fail)
4. findLongestPalindromeBruteForce(palindrome_utils) - (CAUTIONARY FALLBACK)

1.Handling Edge Cases

If the array is empty, return { start: 0, length: 0 }.

If the array contains only one element, return { start: 0, length: 1 }.

2. Transforming the Input Array

This step modifies the input by inserting null between each element and at the boundaries.

Example: If the input was

["red", "blue", "red"]

it transforms into: Note that in some implementations of Manachers, the '#' is used in the place of 'null

["null", "red", "null", "blue", "null", "red", "null"]

This ensures that both odd and even-length palindromes are handled consistently. This due to the fact that Manacher's employes a 'mirroring' technique that operates from a center point and relies on symmetry (Even N means that the center is an x.5 instead of a definite number)

3. Initializing Helper Arrays and Variables

P[i]: Stores the palindrome radius centered at index i. (longest)

Note that radii are used to evaluated the symmetry. Starting at 0 means that the that there is a palindrome of radius 0 (or itsself only) as p[i] is updated as we continue to expand based on symmetry. radius refers to two or n more elements from the center that are symmetrical mirrors of each other

Example:

first transformation with the nulls put in place

[null, "a", null, "b", null, "a", null]

// below is their follow p[i] values from the initialization

P = [0, 0, 0, 0, 0, 0, 0]

// As manacher expands
P = [0, 1, 0, 2, 0, 1, 0] 

center: The center of the current longest palindrome.

right: The right boundary of the palindrome that extends the farthest.

Example

// initial palindrome
transformed = [null, "a", null, "b", null, "a", null]

// Processing index 3 ("b")
// Expands outward: "a, null, b, null, a" 

P[3] = 2 (radius = 2)
// New center = 3, right = 5
// Note: Doesn't count the nulls before the "a" on index 1 and after "a" on index 5
P = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]  // All radii start at 0
center = 0
right = 0

4. Using Manacher’s Algorithm to Find Palindromes

For each position i, check if it’s within the current palindrome (right > i).

If it is, use the mirror value (P[mirror]) to avoid redundant computations.

Otherwise, expand outward from i as long as the transformed elements match.

Updating center and right:

If we find a palindrome that extends beyond right, update center and right.

Example Expansion Process we compare if the left and the right side are symmetrical thus we have these properties: center: 5 radius is 3 let's say we reach green (5) Expand outward (left=4, right=6) → "null" == "null"

Expand again (left=3, right=7) → "blue" == "blue"

Expand again (left=2, right=8) → "null" == "null"

(null) red (null) blue (null) |[green]| (null) blue (null) red (null)

5. Finding the Longest Palindrome

Loop through P[i] to find the maximum palindrome length and its center. After iterating through all indices, we find that:

Max palindrome radius: P[6] = 3

Center index: 6

6. Converting Back to Original Indices

The actual start index in the original array is computed as: Math.floor((centerIndex - maxLen) / 2)

This accounts for the extra null elements.

To convert this back to original indices:

Math.floor((centerIndex - maxLen) / 2)

the function returns the starting index and the length of the longest palindrome In 'palidrome-utils' ["blue", "green", "blue"]


💡Generate Random Sequence

this section covers the explaination for the following:
1.genearateNewSequence() (page.tsx) generates new sequence and updates the color sequence state
2.generateRandomSequence (palindrome_utils.ts) the function that is called by generateNewSequence

1. Turn Count Mechanic

the difficulty of the generated sequence depends on how far the user is in. The higher the turn count the harder the current set up sets this dynamically.

const newSequence = generateRandomSequence(5, 12, turnCount)

// code that implements this
  const difficultyFactor = Math.min(5, Math.floor(turnCount / 3))

// this code adds more noise but does SHOULD NOT DISRUPT THE START OR THE END to preserve integrity
  if (difficultyFactor > 2) {
    // Add some similar colors to confuse the player
    for (let i = 0; i < difficultyFactor; i++) {
      const randomIndex = Math.floor(Math.random() * length)
      if (
        randomIndex !== palindromeStart &&
        randomIndex !== palindromeStart + palindromeLength - 1
      ) {
        colors[randomIndex] = colors[Math.floor(Math.random() * length)]
      }
    }
  }

2. Setting Sequence Length Boundaries

identifies the min/ max length of palindrome COLOR[] sequence

The sequence length starts at minLength but grows with difficulty.A safeguard ensures a minimum of 5 elements. maxLength is adjusted to allow larger sequences as difficulty rises.

const min = Math.max(5, minLength + difficultyFactor);
const max = Math.max(min, maxLength + difficultyFactor);

3. Generating a Random Sequence

const length = Math.floor(Math.random() * (max - min + 1)) + min;
const colors: string[] = [];

The actual sequence length is randomly chosen within the min to max range. An empty array colors is initialized to hold the sequence.

for (let i = 0; i < length; i++) {
    colors.push(COLORS[Math.floor(Math.random() * COLORS.length)].name);
}

A loop fills colors with random names from a predefined COLORS array.

/// this can be modified at anytime
// Define available colors
export const COLORS = [
  { name: 'black', value: '#000000' },
  { name: 'red', value: '#FF0000' },
  { name: 'blue', value: '#0000FF' },
  { name: 'green', value: '#00FF00' },
  { name: 'yellow', value: '#FFFF00' },
  { name: 'purple', value: '#800080' },
  { name: 'orange', value: '#FFA500' },
  { name: 'white', value: '#FFFFFF' },
]

4. Creating Hidden Palindrome

The function ensures the sequence includes at least one palindrome. The palindrome’s length is dynamically calculated to be between 5 and 7 characters. A random starting position for the palindrome is selected. These values can be chosen to be dynamic later on.

const palindromeLength = Math.min(7, Math.max(5, Math.floor(length / 2)));
const palindromeStart = Math.floor(Math.random() * (length - palindromeLength));

🎮 Playing the Game

image

🤝 Contribution

Contributions to the LongDrome Game is welcome! It's small scale for now but can be more complex! Fork this repository and submit a PR to contribute

📜 License

This Project is licensed under the MIT LICENSE

About

🦄Final Project For Algorithms - SE2231 that utilizes the Manacher's algorithm. Powered by NextJS Typescript 🦋

Topics

Resources

License

Stars

Watchers

Forks

Contributors