0% found this document useful (0 votes)
27 views14 pages

Message

Read

Uploaded by

alalie604
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
27 views14 pages

Message

Read

Uploaded by

alalie604
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 14

import pygame

import random
import sys
import os
import math
import numpy as np # Added NumPy library for efficient computations
from pygame.locals import *

# Initialize Pygame
pygame.init()

# Screen dimensions
WIDTH, HEIGHT = 1024, 768
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Advanced N-Back Game with Enhanced 3D Rendering and
NumPy")

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

# Fonts
pygame.font.init()
FONT = pygame.font.SysFont("Verdana", 20)
BIG_FONT = pygame.font.SysFont("Verdana", 36)
TITLE_FONT = pygame.font.SysFont("Verdana", 48)

# Timing
STIMULUS_DURATION = 1000 # milliseconds
INTER_STIMULUS_INTERVAL = 500 # milliseconds
BREAK_DURATION = 10000 # milliseconds (10 seconds break)
TRIALS_BEFORE_BREAK = 20 # Number of trials before a break

# Game modes
MODES = ["Visual N-Back", "Auditory N-Back", "Shape N-Back", "Dual N-Back"]

# Shapes for shape stimuli


SHAPES = ["Circle", "Square", "Triangle", "Star", "Hexagon"]

# Themes
THEMES = {
"Default": {
"BACKGROUND": (30, 30, 30),
"GRID_LINES": (200, 200, 200),
"HIGHLIGHT": (0, 120, 215),
"SHAPE": (173, 216, 230),
"TEXT": WHITE,
"BUTTON": (255, 165, 0),
"BUTTON_HOVER": (255, 215, 0)
},
"Galaxy": {
"BACKGROUND": (10, 10, 30),
"GRID_LINES": (70, 70, 120),
"HIGHLIGHT": (138, 43, 226),
"SHAPE": (75, 0, 130),
"TEXT": WHITE,
"BUTTON": (72, 61, 139),
"BUTTON_HOVER": (123, 104, 238)
},
# Additional themes can be added here
}

class NBackGame:
def __init__(self):
# Player Information
self.player_name = "Player"
self.ask_player_name()

self.N_LEVEL = 2 # Default n-back level


self.sequence = []
self.running = True
self.next_stimulus_time = pygame.time.get_ticks() + STIMULUS_DURATION +
INTER_STIMULUS_INTERVAL
self.user_position_match = False
self.user_sound_match = False
self.user_shape_match = False
self.score = 0
self.high_score = self.load_high_score()
self.scoreboard = self.load_scoreboard()
self.attempted = 0
self.correct = 0
self.total_trials = 0
self.trials_since_break = 0 # Counter for trials since the last break
self.in_break = False # Flag to indicate if currently in a break
self.answer_given = False # Allow only one answer per stimulus
self.state = "menu" # Game states: menu, instructions, settings, game
self.mode = "Visual N-Back" # Default mode
self.is_3d = True # 3D mode flag
self.camera_pos = np.array([0.0, -15.0, -50.0]) # Camera position in 3D
space
self.camera_rot = np.array([0.0, 0.0]) # Camera rotation angles (pitch,
yaw)
self.shapes_enabled = True # Shapes stimuli enabled
self.theme = "Default" # Default theme
self.colors = THEMES[self.theme] # Current color scheme
self.menu_options = []
self.settings_options = []
self.selected_setting = 0
self.instructions_text = []
self.transition_alpha = 255
self.transitioning = False

# Grid and Cell Sizes


self.GRID_SIZE = 10 # 10x10 grid in 2D mode, 10x10x10 in 3D mode
self.CELL_SIZE = 2.0 # Adjusted for 3D rendering
self.GRID_ORIGIN = np.array([0.0, 0.0, 0.0]) # Center of the grid in 3D
space

self.init_gui()

# For 3D rendering
self.fov = 75 # Field of view
self.near_plane = 0.1
self.far_plane = 1000

# Load textures (optional)


self.cube_texture = None # Placeholder for cube texture
# Ask for player's name
def ask_player_name(self):
input_box = pygame.Rect(WIDTH // 2 - 100, HEIGHT // 2 - 25, 200, 50)
active = True
player_name = ''
while active:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
if player_name.strip() != '':
active = False
self.player_name = player_name.strip()
elif event.key == pygame.K_BACKSPACE:
player_name = player_name[:-1]
else:
if len(player_name) < 12:
player_name += event.unicode

SCREEN.fill((30, 30, 30))


prompt_surface = BIG_FONT.render("Enter your name:", True, WHITE)
SCREEN.blit(prompt_surface, (WIDTH // 2 - prompt_surface.get_width() //
2, HEIGHT // 2 - 100))
pygame.draw.rect(SCREEN, WHITE, input_box, 2)
name_surface = FONT.render(player_name, True, WHITE)
SCREEN.blit(name_surface, (input_box.x + 5, input_box.y + 10))
pygame.display.flip()

# Load high score from file


def load_high_score(self):
high_score = 0
try:
if os.path.exists("high_score.txt"):
with open("high_score.txt", "r") as f:
try:
high_score = int(f.read())
except ValueError:
high_score = 0
except:
high_score = 0
return high_score

# Load scoreboard from file


def load_scoreboard(self):
scoreboard = []
try:
if os.path.exists("scoreboard.txt"):
with open("scoreboard.txt", "r") as f:
for line in f:
name, score = line.strip().split(',')
scoreboard.append((name, int(score)))
scoreboard.sort(key=lambda x: x[1], reverse=True)
except:
scoreboard = []
return scoreboard

# Save high score to file


def save_high_score(self):
try:
if self.score > self.high_score:
with open("high_score.txt", "w") as f:
f.write(str(self.score))
self.high_score = self.score # Update the high score in the game
# Update scoreboard
self.scoreboard.append((self.player_name, self.score))
self.scoreboard.sort(key=lambda x: x[1], reverse=True)
self.scoreboard = self.scoreboard[:10] # Keep top 10 scores
with open("scoreboard.txt", "w") as f:
for name, score in self.scoreboard:
f.write(f"{name},{score}\n")
except:
pass

# Initialize GUI components


def init_gui(self):
self.menu_options = [
Button("Play Game", (WIDTH // 2, 250), self.start_game),
Button("Instructions", (WIDTH // 2, 320), self.show_instructions),
Button("Settings", (WIDTH // 2, 390), self.show_settings),
Button("Scoreboard", (WIDTH // 2, 460), self.show_scoreboard),
Button("Quit", (WIDTH // 2, 530), self.quit_game)
]

self.settings_options = [
{"label": "N-Back Level", "value": self.N_LEVEL},
{"label": "Stimulus Duration", "value": f"{STIMULUS_DURATION} ms"},
{"label": "Game Mode", "value": self.mode},
{"label": "3D Mode", "value": "On" if self.is_3d else "Off"},
{"label": "Camera Distance", "value": f"{-self.camera_pos[2]}"},
{"label": "Shapes", "value": "On" if self.shapes_enabled else "Off"},
{"label": "Theme", "value": self.theme},
{"label": "Back", "value": ""}
]

self.update_instructions()

def update_instructions(self):
self.instructions_text = [
f"Welcome, {self.player_name}, to the {self.mode}!",
"In this game, you'll be presented with stimuli.",
f"Your task is to press 'S' if the current stimulus matches the one",
f"from N steps earlier in the sequence (N = {self.N_LEVEL}).",
"Press 'Enter' to go back to the main menu."
]
if self.mode in ["Auditory N-Back", "Dual N-Back"]:
self.instructions_text.insert(4, "Press 'A' if the auditory stimulus
matches.")
if self.shapes_enabled:
self.instructions_text.insert(5, "Press 'D' if the shape stimulus
matches.")

# Draw the game grid with enhanced 3D rendering


def draw_grid(self):
if self.is_3d:
# Prepare the 3D transformation matrices
projection_matrix = self.perspective_matrix(self.fov, WIDTH / HEIGHT,
self.near_plane, self.far_plane)
camera_matrix = self.camera_transform(self.camera_pos, self.camera_rot)

cube_faces = []
# Build cubes for each grid position
for x in range(self.GRID_SIZE):
for y in range(self.GRID_SIZE):
for z in range(self.GRID_SIZE):
cube = self.create_cube(np.array([x, y, z]) -
self.GRID_SIZE / 2)
transformed_faces = []
for face in cube:
transformed_face = []
for vertex in face:
v = self.apply_transformations(vertex,
camera_matrix, projection_matrix)
if v is not None:
transformed_face.append(v)
if len(transformed_face) == 4:
# Calculate average depth for sorting
avg_depth = sum(v[2] for v in transformed_face) /
len(transformed_face)
transformed_faces.append((transformed_face,
avg_depth))
cube_faces.extend(transformed_faces)

# Sort faces by depth (Painter's algorithm)


cube_faces.sort(key=lambda f: f[1], reverse=True)

# Draw faces
for face, _ in cube_faces:
pygame.draw.polygon(SCREEN, self.colors["GRID_LINES"], [(v[0],
v[1]) for v in face], 1)
else:
# Draw 2D grid
for x in range(self.GRID_SIZE + 1):
pygame.draw.line(SCREEN, self.colors["GRID_LINES"], (x * 40, 0), (x
* 40, self.GRID_SIZE * 40), 1)
pygame.draw.line(SCREEN, self.colors["GRID_LINES"], (0, x * 40),
(self.GRID_SIZE * 40, x * 40), 1)

# Create a cube at a given position


def create_cube(self, position):
s = self.CELL_SIZE / 2
vertices = np.array([
position + np.array([-s, -s, -s]),
position + np.array([s, -s, -s]),
position + np.array([s, s, -s]),
position + np.array([-s, s, -s]),
position + np.array([-s, -s, s]),
position + np.array([s, -s, s]),
position + np.array([s, s, s]),
position + np.array([-s, s, s]),
])
faces = [
[vertices[0], vertices[1], vertices[2], vertices[3]], # Back face
[vertices[4], vertices[5], vertices[6], vertices[7]], # Front face
[vertices[0], vertices[1], vertices[5], vertices[4]], # Bottom face
[vertices[2], vertices[3], vertices[7], vertices[6]], # Top face
[vertices[1], vertices[2], vertices[6], vertices[5]], # Right face
[vertices[0], vertices[3], vertices[7], vertices[4]], # Left face
]
return faces

# Apply camera and projection transformations


def apply_transformations(self, vertex, camera_matrix, projection_matrix):
# Convert to homogeneous coordinates
v = np.append(vertex, 1.0)
# Apply camera transformation
v = np.dot(camera_matrix, v)
# Clipping (optional)
if v[2] <= 0:
return None
# Apply projection transformation
v = np.dot(projection_matrix, v)
# Perspective divide
if v[3] != 0:
v = v / v[3]
# Convert to screen coordinates
x = int((v[0] + 1) * WIDTH / 2)
y = int((-v[1] + 1) * HEIGHT / 2)
return (x, y, v[2])

# Create perspective projection matrix


def perspective_matrix(self, fov, aspect, near, far):
f = 1 / math.tan(math.radians(fov) / 2)
range_inv = 1 / (near - far)
return np.array([
[f / aspect, 0, 0, 0],
[0, f, 0, 0],
[0, 0, (near + far) * range_inv, 2 * near * far * range_inv],
[0, 0, -1, 0],
])

# Create camera transformation matrix


def camera_transform(self, position, rotation):
pitch, yaw = rotation
cos_pitch = math.cos(math.radians(pitch))
sin_pitch = math.sin(math.radians(pitch))
cos_yaw = math.cos(math.radians(yaw))
sin_yaw = math.sin(math.radians(yaw))

xaxis = np.array([cos_yaw, 0, -sin_yaw, 0])


yaxis = np.array([sin_yaw * sin_pitch, cos_pitch, cos_yaw * sin_pitch, 0])
zaxis = np.array([sin_yaw * cos_pitch, -sin_pitch, cos_pitch * cos_yaw, 0])
tx = -np.dot(xaxis[:3], position)
ty = -np.dot(yaxis[:3], position)
tz = -np.dot(zaxis[:3], position)

return np.array([
[xaxis[0], yaxis[0], zaxis[0], 0],
[xaxis[1], yaxis[1], zaxis[1], 0],
[xaxis[2], yaxis[2], zaxis[2], 0],
[tx, ty, tz, 1]
])

# Draw the highlighted stimulus


def draw_stimulus(self, stimulus):
if self.is_3d:
x, y, z = stimulus["position"]
# Prepare the 3D transformation matrices
projection_matrix = self.perspective_matrix(self.fov, WIDTH / HEIGHT,
self.near_plane, self.far_plane)
camera_matrix = self.camera_transform(self.camera_pos, self.camera_rot)

# Create cube at stimulus position


cube = self.create_cube(np.array([x, y, z]) - self.GRID_SIZE / 2)
cube_faces = []
for face in cube:
transformed_face = []
for vertex in face:
v = self.apply_transformations(vertex, camera_matrix,
projection_matrix)
if v is not None:
transformed_face.append(v)
if len(transformed_face) == 4:
avg_depth = sum(v[2] for v in transformed_face) /
len(transformed_face)
cube_faces.append((transformed_face, avg_depth))

# Sort faces by depth


cube_faces.sort(key=lambda f: f[1], reverse=True)

# Draw faces with color


for face, _ in cube_faces:
pygame.draw.polygon(SCREEN, self.colors["HIGHLIGHT"], [(v[0], v[1])
for v in face])
else:
x, y = stimulus["position"]
rect = pygame.Rect(x * 40, y * 40, 40, 40)
pygame.draw.rect(SCREEN, self.colors["HIGHLIGHT"], rect)

# Draw shape if enabled


if self.shapes_enabled:
shape = stimulus["shape"]
center = rect.center
size = 20
if shape == "Circle":
pygame.draw.circle(SCREEN, self.colors["SHAPE"], center, size)
elif shape == "Square":
square_rect = pygame.Rect(0, 0, size * 2, size * 2)
square_rect.center = center
pygame.draw.rect(SCREEN, self.colors["SHAPE"], square_rect)
elif shape == "Triangle":
points = [
(center[0], center[1] - size),
(center[0] - size, center[1] + size),
(center[0] + size, center[1] + size)
]
pygame.draw.polygon(SCREEN, self.colors["SHAPE"], points)
# Other shapes can be added similarly

# Get a random stimulus


def get_random_stimulus(self):
if self.is_3d:
positions_3d = [(x, y, z) for x in range(self.GRID_SIZE) for y in
range(self.GRID_SIZE) for z in range(self.GRID_SIZE)]
position = random.choice(positions_3d)
else:
positions_2d = [(x, y) for x in range(self.GRID_SIZE) for y in
range(self.GRID_SIZE)]
position = random.choice(positions_2d)
sound = random.choice([0]) # Placeholder for sound frequencies
shape = random.choice(SHAPES)
return {"position": position, "sound": sound, "shape": shape}

# Check if the player's response matches the correct answer


def check_match(self):
if len(self.sequence) > self.N_LEVEL:
prev_stimulus = self.sequence[-(self.N_LEVEL + 1)]
curr_stimulus = self.sequence[-1]
position_match = prev_stimulus["position"] == curr_stimulus["position"]
sound_match = prev_stimulus["sound"] == curr_stimulus["sound"]
shape_match = prev_stimulus["shape"] == curr_stimulus["shape"]

if self.user_position_match:
self.attempted += 1
if position_match:
self.score += 1
self.correct += 1
else:
self.score -= 1
else:
if position_match:
# Missed a match
self.score -= 1

if self.user_sound_match:
self.attempted += 1
if sound_match:
self.score += 1
self.correct += 1
else:
self.score -= 1
else:
if sound_match:
# Missed a match
self.score -= 1

if self.user_shape_match and self.shapes_enabled:


self.attempted += 1
if shape_match:
self.score += 1
self.correct += 1
else:
self.score -= 1
else:
if shape_match and self.shapes_enabled:
# Missed a match
self.score -= 1
else:
# Not enough elements to compare
if self.user_position_match or self.user_sound_match or
self.user_shape_match:
self.score -= 1 # Penalize for incorrect guess
self.attempted += 1
# Display score and high score
def display_score(self):
score_surface = FONT.render(f"Score: {self.score}", True,
self.colors["TEXT"])
SCREEN.blit(score_surface, (10, HEIGHT - 150))
high_score_surface = FONT.render(f"High Score: {self.high_score}", True,
self.colors["TEXT"])
SCREEN.blit(high_score_surface, (200, HEIGHT - 150))
n_level_surface = FONT.render(f"N-Back Level: {self.N_LEVEL}", True,
self.colors["TEXT"])
SCREEN.blit(n_level_surface, (400, HEIGHT - 150))

# Display performance statistics


def display_statistics(self):
accuracy = (self.correct / self.attempted * 100) if self.attempted > 0 else
0
stats = [
f"Total Trials: {self.total_trials}",
f"Attempts: {self.attempted}",
f"Correct: {self.correct}",
f"Accuracy: {accuracy:.2f}%",
]
for i, line in enumerate(stats):
text_surface = FONT.render(line, True, self.colors["TEXT"])
SCREEN.blit(text_surface, (10, HEIGHT - 110 + i * 30))

# Display the main menu


def display_menu(self):
SCREEN.fill(self.colors["BACKGROUND"])
# Draw title
title_surface = TITLE_FONT.render("N-Back Game", True, self.colors["TEXT"])
SCREEN.blit(title_surface, (WIDTH // 2 - title_surface.get_width() // 2,
100))
# Draw buttons
for button in self.menu_options:
button.draw(SCREEN, self.colors)
pygame.display.flip()

# Display the instructions screen


def display_instructions(self):
SCREEN.fill(self.colors["BACKGROUND"])
title_surface = BIG_FONT.render("Instructions", True, self.colors["TEXT"])
SCREEN.blit(title_surface, (WIDTH // 2 - title_surface.get_width() // 2,
50))
for i, line in enumerate(self.instructions_text):
text_surface = FONT.render(line, True, self.colors["TEXT"])
SCREEN.blit(text_surface, (50, 150 + i * 30))
pygame.display.flip()

# Display the settings menu


def display_settings(self):
SCREEN.fill(self.colors["BACKGROUND"])
title_surface = BIG_FONT.render("Settings", True, self.colors["TEXT"])
SCREEN.blit(title_surface, (WIDTH // 2 - title_surface.get_width() // 2,
50))
for i, option in enumerate(self.settings_options):
color = self.colors["BUTTON_HOVER"] if i == self.selected_setting else
self.colors["TEXT"]
label = option["label"]
value = option["value"]
option_surface = FONT.render(f"{label}: {value}", True, color)
SCREEN.blit(option_surface, (WIDTH // 2 - option_surface.get_width() //
2, 150 + i * 50))
pygame.display.flip()

# Display the scoreboard


def display_scoreboard(self):
SCREEN.fill(self.colors["BACKGROUND"])
title_surface = BIG_FONT.render("Scoreboard", True, self.colors["TEXT"])
SCREEN.blit(title_surface, (WIDTH // 2 - title_surface.get_width() // 2,
50))
for i, (name, score) in enumerate(self.scoreboard):
entry_surface = FONT.render(f"{i+1}. {name}: {score}", True,
self.colors["TEXT"])
SCREEN.blit(entry_surface, (WIDTH // 2 - entry_surface.get_width() //
2, 150 + i * 30))
pygame.display.flip()
# Wait for user to press Enter to return to menu
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == QUIT:
self.running = False
waiting = False
elif event.type == KEYDOWN and event.key == K_RETURN:
self.transition_to("menu")
waiting = False

# Handle events in the main menu


def handle_menu_events(self):
for event in pygame.event.get():
if event.type == QUIT:
self.running = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
self.running = False
for button in self.menu_options:
button.check_event(event)

# Handle events in the instructions screen


def handle_instructions_events(self):
for event in pygame.event.get():
if event.type == QUIT:
self.running = False
elif event.type == KEYDOWN and event.key == K_RETURN:
self.transition_to("menu")

# Handle events in the settings menu


def handle_settings_events(self):
global STIMULUS_DURATION
for event in pygame.event.get():
if event.type == QUIT:
self.running = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
self.transition_to("menu")
elif event.key == K_UP:
self.selected_setting = (self.selected_setting - 1) %
len(self.settings_options)
elif event.key == K_DOWN:
self.selected_setting = (self.selected_setting + 1) %
len(self.settings_options)
elif event.key == K_LEFT or event.key == K_RIGHT:
increment = 1 if event.key == K_RIGHT else -1
if self.selected_setting == 0:
if 1 <= self.N_LEVEL + increment <= 9:
self.N_LEVEL += increment
self.settings_options[0]["value"] = self.N_LEVEL
self.update_instructions()
elif self.selected_setting == 1:
if 500 <= STIMULUS_DURATION + (increment * 100) <= 3000:
STIMULUS_DURATION += increment * 100
self.settings_options[1]["value"] =
f"{STIMULUS_DURATION} ms"
elif self.selected_setting == 2:
mode_index = MODES.index(self.mode)
mode_index = (mode_index + increment) % len(MODES)
self.mode = MODES[mode_index]
self.settings_options[2]["value"] = self.mode
self.update_instructions()
elif self.selected_setting == 3:
self.is_3d = not self.is_3d
self.settings_options[3]["value"] = "On" if self.is_3d else
"Off"
elif self.selected_setting == 4:
if -100 <= self.camera_pos[2] - (increment * 5) <= -5:
self.camera_pos[2] -= increment * 5
self.settings_options[4]["value"] = f"{-
self.camera_pos[2]}"
elif self.selected_setting == 5:
self.shapes_enabled = not self.shapes_enabled
self.settings_options[5]["value"] = "On" if
self.shapes_enabled else "Off"
self.update_instructions()
elif self.selected_setting == 6:
theme_keys = list(THEMES.keys())
current_index = theme_keys.index(self.theme)
new_index = (current_index + increment) % len(theme_keys)
self.theme = theme_keys[new_index]
self.colors = THEMES[self.theme]
self.settings_options[6]["value"] = self.theme
elif self.selected_setting == 7:
pass # Back option
elif event.key == K_RETURN:
if self.settings_options[self.selected_setting]["label"] ==
"Back":
self.transition_to("menu")

# Handle events during the game


def handle_game_events(self):
for event in pygame.event.get():
if event.type == QUIT:
self.save_high_score()
self.running = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
self.save_high_score()
self.transition_to("menu")
elif event.key == K_s and not self.answer_given:
self.user_position_match = True
self.answer_given = True # Prevent multiple answers
self.check_match()
elif event.key == K_a and not self.answer_given:
self.user_sound_match = True
self.answer_given = True # Prevent multiple answers
self.check_match()
elif event.key == K_d and not self.answer_given and
self.shapes_enabled:
self.user_shape_match = True
self.answer_given = True
self.check_match()

# Reset game variables for a new game


def reset_game(self):
self.sequence = []
self.score = 0
self.attempted = 0
self.correct = 0
self.total_trials = 0
self.trials_since_break = 0
self.in_break = False
self.next_stimulus_time = pygame.time.get_ticks() + STIMULUS_DURATION +
INTER_STIMULUS_INTERVAL
self.answer_given = False
self.transition_alpha = 255
self.transitioning = True

# Start the game


def start_game(self):
self.reset_game()
self.transition_to("game")

# Show instructions
def show_instructions(self):
self.transition_to("instructions")

# Show settings
def show_settings(self):
self.transition_to("settings")

# Show scoreboard
def show_scoreboard(self):
self.transition_to("scoreboard")

# Quit the game


def quit_game(self):
self.running = False

# Transition between game states


def transition_to(self, state):
self.state = state
self.transition_alpha = 255
self.transitioning = True

# Main game loop


def main_loop(self):
clock = pygame.time.Clock()

while self.running:
if self.transitioning:
self.transition_alpha -= 5
if self.transition_alpha <= 0:
self.transition_alpha = 0
self.transitioning = False

if self.state == "menu":
self.display_menu()
self.handle_menu_events()
elif self.state == "instructions":
self.display_instructions()
self.handle_instructions_events()
elif self.state == "settings":
self.display_settings()
self.handle_settings_events()
elif self.state == "scoreboard":
self.display_scoreboard()
elif self.state == "game":
SCREEN.fill(self.colors["BACKGROUND"])
self.draw_grid()
self.display_score()
self.display_statistics()

current_time = pygame.time.get_ticks()

if self.in_break:
if current_time >= self.break_end_time:
self.in_break = False
self.next_stimulus_time = current_time +
INTER_STIMULUS_INTERVAL
else:
# Display "Break" during the break period
break_surface = BIG_FONT.render("Break", True,
self.colors["TEXT"])
SCREEN.blit(break_surface, (WIDTH // 2 -
break_surface.get_width() // 2, HEIGHT // 2 - break_surface.get_height() // 2))
else:
if current_time >= self.next_stimulus_time:
# Check if we need to initiate a break
if self.trials_since_break >= TRIALS_BEFORE_BREAK:
self.in_break = True
self.break_end_time = current_time + BREAK_DURATION
self.trials_since_break = 0
else:
# Get new stimulus
stimulus = self.get_random_stimulus()
self.sequence.append(stimulus)
self.total_trials += 1
self.trials_since_break += 1

# Reset user input


self.user_position_match = False
self.user_sound_match = False
self.user_shape_match = False
self.answer_given = False
self.next_stimulus_time = current_time +
STIMULUS_DURATION + INTER_STIMULUS_INTERVAL

# Play auditory stimulus if applicable


if self.mode in ["Auditory N-Back", "Dual N-Back"]:
frequency = stimulus["sound"]
# Placeholder for sound playback

# Display stimulus for a limited time


time_since_last_stimulus = current_time -
(self.next_stimulus_time - STIMULUS_DURATION - INTER_STIMULUS_INTERVAL)
if self.sequence and time_since_last_stimulus <
STIMULUS_DURATION:
stimulus = self.sequence[-1]
if self.mode in ["Visual N-Back", "Dual N-Back"]:
self.draw_stimulus(stimulus)

self.handle_game_events()

# Apply fade-in transition


if self.transitioning:
overlay = pygame.Surface((WIDTH, HEIGHT))
overlay.set_alpha(self.transition_alpha)
overlay.fill(BLACK)
SCREEN.blit(overlay, (0, 0))

pygame.display.flip()
clock.tick(60)

pygame.quit()
sys.exit()

class Button:
def __init__(self, text, pos, callback):
self.text = text
self.pos = pos
self.callback = callback
self.rect = pygame.Rect(0, 0, 250, 50)
self.rect.center = pos
self.hovered = False

def draw(self, screen, colors):


color = colors["BUTTON_HOVER"] if self.hovered else colors["BUTTON"]
pygame.draw.rect(screen, color, self.rect, border_radius=12)
text_surface = FONT.render(self.text, True, BLACK)
text_rect = text_surface.get_rect(center=self.rect.center)
screen.blit(text_surface, text_rect)

def check_event(self, event):


if event.type == MOUSEMOTION:
self.hovered = self.rect.collidepoint(event.pos)
elif event.type == MOUSEBUTTONDOWN:
if self.hovered:
self.callback()

if __name__ == "__main__":
game = NBackGame()
game.main_loop()

You might also like