clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

main
puzzletea / games / lightsout / elo_test.go
elo_test.go 3.3K
1package lightsout23import (4 "fmt"5 "reflect"6 "testing"78 "github.com/FelineStateMachine/puzzletea/difficulty"9)1011func TestSpawnEloRejectsInvalidElo(t *testing.T) {12 mode := NewMode("Elo", "test", 5, 5)1314 tests := []difficulty.Elo{15 difficulty.MinElo - 1,16 difficulty.SoftCapElo + 1,17 }18 for _, elo := range tests {19 t.Run(fmt.Sprint(elo), func(t *testing.T) {20 gamer, report, err := mode.SpawnElo("seed", elo)21 if err == nil {22 t.Fatalf("SpawnElo(%d) error = nil, want invalid Elo error", elo)23 }24 if gamer != nil {25 t.Fatalf("SpawnElo(%d) gamer = %#v, want nil", elo, gamer)26 }27 if !reflect.DeepEqual(report, difficulty.Report{}) {28 t.Fatalf("SpawnElo(%d) report = %#v, want zero report", elo, report)29 }30 })31 }32}3334func TestSpawnEloDeterministicForSameSeedAndElo(t *testing.T) {35 mode := NewMode("Elo", "test", 5, 5)36 const seed = "lightsout-deterministic"37 const elo = difficulty.Elo(1700)3839 gamerA, reportA, err := mode.SpawnElo(seed, elo)40 if err != nil {41 t.Fatalf("first SpawnElo returned error: %v", err)42 }43 gamerB, reportB, err := mode.SpawnElo(seed, elo)44 if err != nil {45 t.Fatalf("second SpawnElo returned error: %v", err)46 }4748 saveA, err := gamerA.(Model).GetSave()49 if err != nil {50 t.Fatalf("first GetSave returned error: %v", err)51 }52 saveB, err := gamerB.(Model).GetSave()53 if err != nil {54 t.Fatalf("second GetSave returned error: %v", err)55 }5657 if string(saveA) != string(saveB) {58 t.Fatalf("SpawnElo saves differ for same seed and Elo:\n%s\n%s", saveA, saveB)59 }60 if !reflect.DeepEqual(reportA, reportB) {61 t.Fatalf("report mismatch for same seed/Elo:\n%#v\n\n%#v", reportA, reportB)62 }63}6465func TestSpawnEloPopulatesDifficultyReport(t *testing.T) {66 mode := NewMode("Elo", "test", 5, 5)67 const target = difficulty.Elo(2200)6869 gamer, report, err := mode.SpawnElo("report-fields", target)70 if err != nil {71 t.Fatalf("SpawnElo returned error: %v", err)72 }73 if gamer == nil {74 t.Fatal("SpawnElo returned nil gamer")75 }76 if report.TargetElo != target {77 t.Fatalf("TargetElo = %d, want %d", report.TargetElo, target)78 }79 if err := difficulty.ValidateElo(report.ActualElo); err != nil {80 t.Fatalf("ActualElo = %d is invalid: %v", report.ActualElo, err)81 }82 if report.Confidence == "" {83 t.Fatal("Confidence is empty")84 }8586 requiredMetrics := map[string]float64{87 "width": 1,88 "height": 1,89 "cells": 1,90 "lights_on": 1,91 "light_density": 0,92 "min_solution_moves": 1,93 "move_density": 0,94 "solvable": 1,95 }96 for key, min := range requiredMetrics {97 got, ok := report.Metrics[key]98 if !ok {99 t.Fatalf("Metrics[%q] missing from %#v", key, report.Metrics)100 }101 if got < min {102 t.Fatalf("Metrics[%q] = %v, want >= %v", key, got, min)103 }104 }105}106107func TestSpawnEloPreservesSelectedDimensions(t *testing.T) {108 mode := NewMode("Easy", "3x3 grid", 3, 3)109 for _, elo := range []difficulty.Elo{100, 2800} {110 _, report, err := mode.SpawnElo("fixed-size", elo)111 if err != nil {112 t.Fatalf("SpawnElo(%d) returned error: %v", elo, err)113 }114 if got, want := report.Metrics["width"], 3.0; got != want {115 t.Fatalf("SpawnElo(%d) width metric = %.0f, want %.0f", elo, got, want)116 }117 if got, want := report.Metrics["height"], 3.0; got != want {118 t.Fatalf("SpawnElo(%d) height metric = %.0f, want %.0f", elo, got, want)119 }120 }121}