You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
476 lines
12 KiB
Markdown
476 lines
12 KiB
Markdown
# Know Foolery - Game Mechanics Documentation
|
|
|
|
## Overview
|
|
|
|
Know Foolery is a quiz game where players answer questions to earn points within a time-limited session. The game emphasizes strategic thinking through its hint system and attempt limitations.
|
|
|
|
## Core Game Flow
|
|
|
|
### 1. Game Session Initialization
|
|
```
|
|
Player Input: Name → Session Creation → Question Selection → Game Start
|
|
```
|
|
|
|
**Process:**
|
|
1. Player enters their name (required)
|
|
2. System creates new game session
|
|
3. System randomly selects first question
|
|
4. 30-minute timer starts
|
|
5. Game begins with question display
|
|
|
|
**Validation:**
|
|
- Player name: 2-50 characters, alphanumeric + spaces
|
|
- Session uniqueness: One active session per player
|
|
- Timer initialization: Server-side timestamp
|
|
|
|
### 2. Question Presentation
|
|
|
|
**Question Structure:**
|
|
```typescript
|
|
interface Question {
|
|
id: string
|
|
theme: string // e.g., "Geography", "History", "Science"
|
|
text: string // The actual question
|
|
answer: string // Correct answer (case-insensitive)
|
|
hint: string // Optional hint text
|
|
difficulty?: number // Future enhancement
|
|
}
|
|
```
|
|
|
|
**Display Format:**
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ Theme: Geography ⏱️ 28:45 │
|
|
├─────────────────────────────────────┤
|
|
│ │
|
|
│ What is the capital of France? │
|
|
│ │
|
|
│ [Your answer here... ] 💡 │
|
|
│ │
|
|
│ Attempts remaining: 3/3 │
|
|
│ Current score: 4 points │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
### 3. Answer Submission Process
|
|
|
|
**Player Actions:**
|
|
1. **Direct Answer**: Submit answer without hint
|
|
2. **Request Hint**: View hint before answering
|
|
3. **Skip Question**: Move to next question (future enhancement)
|
|
|
|
**Answer Validation:**
|
|
```go
|
|
// Server-side validation logic
|
|
func ValidateAnswer(userAnswer, correctAnswer string) bool {
|
|
// Normalize both answers
|
|
user := strings.ToLower(strings.TrimSpace(userAnswer))
|
|
correct := strings.ToLower(strings.TrimSpace(correctAnswer))
|
|
|
|
// Exact match
|
|
if user == correct {
|
|
return true
|
|
}
|
|
|
|
// Fuzzy matching (future enhancement)
|
|
similarity := calculateSimilarity(user, correct)
|
|
return similarity > 0.85
|
|
}
|
|
```
|
|
|
|
## Scoring System
|
|
|
|
### Point Values
|
|
- **Correct without hint**: 2 points
|
|
- **Correct with hint**: 1 point
|
|
- **Incorrect answer**: 0 points
|
|
- **Session timeout**: 0 points for remaining questions
|
|
|
|
### Scoring Logic
|
|
```go
|
|
type AttemptResult struct {
|
|
IsCorrect bool
|
|
UsedHint bool
|
|
AttemptNum int // 1, 2, or 3
|
|
}
|
|
|
|
func CalculateScore(result AttemptResult) int {
|
|
if !result.IsCorrect {
|
|
return 0
|
|
}
|
|
|
|
if result.UsedHint {
|
|
return 1 // Reduced score for using hint
|
|
}
|
|
|
|
return 2 // Full score for direct correct answer
|
|
}
|
|
```
|
|
|
|
### Score Tracking
|
|
```go
|
|
type GameSession struct {
|
|
PlayerName string
|
|
TotalScore int
|
|
QuestionsAsked int
|
|
QuestionsCorrect int
|
|
HintsUsed int
|
|
StartTime time.Time
|
|
EndTime *time.Time
|
|
IsActive bool
|
|
}
|
|
```
|
|
|
|
## Attempt System
|
|
|
|
### Attempt Rules
|
|
- **Maximum Attempts**: 3 per question
|
|
- **Attempt Tracking**: Server-side validation
|
|
- **Attempt Consequences**:
|
|
- Attempt 1-3: Normal scoring if correct
|
|
- After 3 failed attempts: Move to next question automatically
|
|
|
|
### Attempt Flow
|
|
```go
|
|
type QuestionAttempt struct {
|
|
SessionID string
|
|
QuestionID string
|
|
AttemptNum int // 1, 2, or 3
|
|
Answer string
|
|
IsCorrect bool
|
|
UsedHint bool
|
|
Timestamp time.Time
|
|
}
|
|
|
|
func ProcessAttempt(sessionID, questionID, answer string) AttemptResult {
|
|
// 1. Validate session is active
|
|
// 2. Check attempts remaining
|
|
// 3. Validate answer
|
|
// 4. Update score if correct
|
|
// 5. Move to next question if needed
|
|
}
|
|
```
|
|
|
|
## Hint System
|
|
|
|
### Hint Mechanics
|
|
- **Availability**: One hint per question
|
|
- **Timing**: Can be requested before any attempt
|
|
- **Impact**: Reduces maximum score from 2 to 1 points
|
|
- **Content**: Provides helpful clue without giving away answer
|
|
|
|
### Hint Implementation
|
|
```typescript
|
|
interface HintRequest {
|
|
sessionId: string
|
|
questionId: string
|
|
}
|
|
|
|
interface HintResponse {
|
|
hint: string
|
|
scoreImpact: string // "Using this hint will reduce your score to 1 point"
|
|
attemptsRemaining: number
|
|
}
|
|
```
|
|
|
|
### Hint Examples
|
|
```javascript
|
|
const hintExamples = {
|
|
question: "What is the capital of France?",
|
|
hint: "This city is famous for the Eiffel Tower and is located on the Seine River.",
|
|
|
|
question: "Who painted the Mona Lisa?",
|
|
hint: "This Italian Renaissance artist was also an inventor and scientist.",
|
|
|
|
question: "What is the largest planet in our solar system?",
|
|
hint: "This gas giant is named after the king of the Roman gods."
|
|
}
|
|
```
|
|
|
|
## Time Management
|
|
|
|
### Session Timer
|
|
- **Duration**: 30 minutes maximum per session
|
|
- **Display**: Real-time countdown timer
|
|
- **Warnings**: Visual alerts at 5 minutes and 1 minute remaining
|
|
- **Timeout**: Automatic session termination when time expires
|
|
|
|
### Timer Implementation
|
|
```go
|
|
type SessionTimer struct {
|
|
SessionID string
|
|
StartTime time.Time
|
|
Duration time.Duration // 30 minutes
|
|
WarningsSent map[int]bool // Track warnings sent
|
|
}
|
|
|
|
func (t *SessionTimer) TimeRemaining() time.Duration {
|
|
elapsed := time.Since(t.StartTime)
|
|
remaining := t.Duration - elapsed
|
|
if remaining < 0 {
|
|
return 0
|
|
}
|
|
return remaining
|
|
}
|
|
|
|
func (t *SessionTimer) IsExpired() bool {
|
|
return t.TimeRemaining() == 0
|
|
}
|
|
```
|
|
|
|
### Timer Events
|
|
```typescript
|
|
interface TimerEvent {
|
|
type: 'warning' | 'timeout'
|
|
timeRemaining: number // seconds
|
|
message: string
|
|
}
|
|
|
|
// Timer warnings
|
|
const timerWarnings = [
|
|
{ at: 300, message: "5 minutes remaining!" }, // 5 minutes
|
|
{ at: 60, message: "1 minute remaining!" }, // 1 minute
|
|
{ at: 10, message: "10 seconds left!" } // 10 seconds
|
|
]
|
|
```
|
|
|
|
## Question Selection
|
|
|
|
### Random Selection Algorithm
|
|
```go
|
|
func SelectRandomQuestion(sessionID string, excludeAnswered bool) (*Question, error) {
|
|
var questions []Question
|
|
query := db.Question.Query()
|
|
|
|
if excludeAnswered {
|
|
// Exclude questions already answered in this session
|
|
answeredIDs := getAnsweredQuestions(sessionID)
|
|
query = query.Where(question.IDNotIn(answeredIDs...))
|
|
}
|
|
|
|
questions, err := query.All(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(questions) == 0 {
|
|
return nil, errors.New("no more questions available")
|
|
}
|
|
|
|
// Random selection
|
|
randomIndex := rand.Intn(len(questions))
|
|
return &questions[randomIndex], nil
|
|
}
|
|
```
|
|
|
|
### Question Difficulty (Future Enhancement)
|
|
```go
|
|
type DifficultyLevel int
|
|
|
|
const (
|
|
Easy DifficultyLevel = iota + 1
|
|
Medium
|
|
Hard
|
|
)
|
|
|
|
// Adaptive difficulty based on player performance
|
|
func SelectQuestionByDifficulty(sessionID string, targetDifficulty DifficultyLevel) (*Question, error) {
|
|
// Implementation for adaptive difficulty
|
|
// Consider player's success rate, current score, etc.
|
|
}
|
|
```
|
|
|
|
## Leaderboard System
|
|
|
|
### Leaderboard Calculation
|
|
```go
|
|
type LeaderboardEntry struct {
|
|
PlayerName string
|
|
Score int
|
|
QuestionsAnswered int
|
|
SuccessRate float64 // Percentage of correct answers
|
|
SessionDuration time.Duration
|
|
CompletedAt time.Time
|
|
}
|
|
|
|
func CalculateLeaderboard() ([]LeaderboardEntry, error) {
|
|
sessions := getCompletedSessions()
|
|
|
|
entries := make([]LeaderboardEntry, 0, len(sessions))
|
|
for _, session := range sessions {
|
|
entry := LeaderboardEntry{
|
|
PlayerName: session.PlayerName,
|
|
Score: session.TotalScore,
|
|
QuestionsAnswered: session.QuestionsAsked,
|
|
SuccessRate: float64(session.QuestionsCorrect) / float64(session.QuestionsAsked) * 100,
|
|
SessionDuration: session.EndTime.Sub(session.StartTime),
|
|
CompletedAt: *session.EndTime,
|
|
}
|
|
entries = append(entries, entry)
|
|
}
|
|
|
|
// Sort by score (descending), then by completion time (ascending)
|
|
sort.Slice(entries, func(i, j int) bool {
|
|
if entries[i].Score == entries[j].Score {
|
|
return entries[i].CompletedAt.Before(entries[j].CompletedAt)
|
|
}
|
|
return entries[i].Score > entries[j].Score
|
|
})
|
|
|
|
// Return top 10
|
|
if len(entries) > 10 {
|
|
entries = entries[:10]
|
|
}
|
|
|
|
return entries, nil
|
|
}
|
|
```
|
|
|
|
### Leaderboard Display
|
|
```
|
|
🏆 LEADERBOARD
|
|
|
|
Rank | Player | Score | Questions | Success Rate | Duration
|
|
-----|-----------|-------|-----------|--------------|----------
|
|
1 | Alice | 24 | 14 | 86% | 28m
|
|
2 | Bob | 22 | 13 | 85% | 25m
|
|
3 | Charlie | 20 | 12 | 83% | 30m
|
|
4 | Diana | 18 | 11 | 82% | 22m
|
|
5 | Eve | 16 | 10 | 80% | 27m
|
|
```
|
|
|
|
## Game State Management
|
|
|
|
### Session States
|
|
```go
|
|
type SessionState int
|
|
|
|
const (
|
|
SessionCreated SessionState = iota
|
|
SessionActive
|
|
SessionPaused // Future enhancement
|
|
SessionCompleted
|
|
SessionTimedOut
|
|
SessionAbandoned
|
|
)
|
|
|
|
type GameSession struct {
|
|
ID string
|
|
PlayerName string
|
|
State SessionState
|
|
CurrentQuestion *Question
|
|
Score int
|
|
// ... other fields
|
|
}
|
|
```
|
|
|
|
### State Transitions
|
|
```
|
|
Created → Active → {Completed, TimedOut, Abandoned}
|
|
↓
|
|
Paused → Active (future enhancement)
|
|
```
|
|
|
|
## Anti-Cheating Measures
|
|
|
|
### Server-Side Validation
|
|
```go
|
|
type SecurityCheck struct {
|
|
SessionID string
|
|
QuestionID string
|
|
ExpectedAnswer string
|
|
SubmittedAnswer string
|
|
TimestampSubmit time.Time
|
|
TimestampShow time.Time
|
|
IsValid bool
|
|
Reason string
|
|
}
|
|
|
|
func ValidateSubmission(check SecurityCheck) bool {
|
|
// 1. Check minimum time between question show and answer submit
|
|
minTime := 2 * time.Second
|
|
if check.TimestampSubmit.Sub(check.TimestampShow) < minTime {
|
|
check.Reason = "Answer submitted too quickly"
|
|
return false
|
|
}
|
|
|
|
// 2. Validate session state
|
|
session := getSession(check.SessionID)
|
|
if session.State != SessionActive {
|
|
check.Reason = "Invalid session state"
|
|
return false
|
|
}
|
|
|
|
// 3. Check if question belongs to session
|
|
if session.CurrentQuestion.ID != check.QuestionID {
|
|
check.Reason = "Question mismatch"
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
```
|
|
|
|
### Client-Side Integrity
|
|
```typescript
|
|
// Prevent common cheating attempts
|
|
class GameIntegrity {
|
|
private questionStartTime: number = 0
|
|
|
|
onQuestionDisplayed() {
|
|
this.questionStartTime = Date.now()
|
|
|
|
// Disable browser dev tools (basic deterrent)
|
|
this.disableDevTools()
|
|
|
|
// Prevent copy-paste in answer input
|
|
this.disableCopyPaste()
|
|
}
|
|
|
|
onAnswerSubmit(answer: string) {
|
|
const timeTaken = Date.now() - this.questionStartTime
|
|
|
|
// Include timing in submission for server validation
|
|
return {
|
|
answer,
|
|
timeTaken,
|
|
timestamp: Date.now()
|
|
}
|
|
}
|
|
|
|
private disableDevTools() {
|
|
// Basic dev tools detection (not foolproof)
|
|
const devtools = {
|
|
open: false,
|
|
orientation: null
|
|
}
|
|
|
|
const threshold = 160
|
|
setInterval(() => {
|
|
if (window.outerHeight - window.innerHeight > threshold ||
|
|
window.outerWidth - window.innerWidth > threshold) {
|
|
console.warn('Developer tools detected')
|
|
// Log security event
|
|
}
|
|
}, 500)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Future Enhancements
|
|
|
|
### Planned Features
|
|
1. **Question Categories**: Filter questions by subject
|
|
2. **Difficulty Progression**: Adaptive difficulty based on performance
|
|
3. **Multiplayer Mode**: Real-time competition between players
|
|
4. **Daily Challenges**: Special themed question sets
|
|
5. **Achievement System**: Badges and milestones
|
|
6. **Question Contributions**: Player-submitted questions
|
|
|
|
### Scoring Enhancements
|
|
1. **Time Bonus**: Extra points for quick correct answers
|
|
2. **Streak Bonus**: Consecutive correct answers bonus
|
|
3. **Difficulty Multiplier**: Higher points for harder questions
|
|
4. **Perfect Game Bonus**: Bonus for 100% correct rate
|
|
|
|
This game mechanics documentation ensures consistent implementation across all platforms and provides clear guidelines for future enhancements. |