clone

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

feat/elo-difficulty
puzzletea / games / netwalk / gamemode.go
gamemode.go 3.7K
1package netwalk23import (4 _ "embed"5 "math/rand/v2"67 "github.com/FelineStateMachine/puzzletea/difficulty"8 "github.com/FelineStateMachine/puzzletea/game"9 "github.com/FelineStateMachine/puzzletea/gameentry"10 "github.com/FelineStateMachine/puzzletea/puzzle"11)1213//go:embed help.md14var HelpContent string1516type NetwalkMode struct {17 game.BaseMode18 Size int19 FillRatio float6420 Profile generateProfile21}2223var (24 _ game.Mode = NetwalkMode{}25 _ game.Spawner = NetwalkMode{}26 _ game.SeededSpawner = NetwalkMode{}27 _ game.EloSpawner = NetwalkMode{}28)2930func NewMode(title, desc string, size int, fillRatio float64, profile generateProfile) NetwalkMode {31 return NetwalkMode{32 BaseMode: game.NewBaseMode(title, desc),33 Size: size,34 FillRatio: fillRatio,35 Profile: profile,36 }37}3839func (m NetwalkMode) Spawn() (game.Gamer, error) {40 p, err := GenerateSeededWithDensity(41 m.Size,42 m.FillRatio,43 m.Profile,44 rand.New(rand.NewPCG(rand.Uint64(), rand.Uint64())),45 )46 if err != nil {47 return nil, err48 }49 return New(m, p)50}5152func (m NetwalkMode) SpawnSeeded(rng *rand.Rand) (game.Gamer, error) {53 p, err := GenerateSeededWithDensity(m.Size, m.FillRatio, m.Profile, rng)54 if err != nil {55 return nil, err56 }57 return New(m, p)58}5960var (61 miniProfile = generateProfile{62 ParentDegreeWeights: [5]int{16, 16, 6, -8, -12},63 OrthogonalPackedWeight: 2,64 DiagonalPackedWeight: 1,65 SpanGrowthWeight: 12,66 MinSpanRatio: 0.55,67 }68 easyProfile = generateProfile{69 ParentDegreeWeights: [5]int{14, 14, 8, -4, -8},70 OrthogonalPackedWeight: 4,71 DiagonalPackedWeight: 2,72 SpanGrowthWeight: 14,73 MinSpanRatio: 0.62,74 }75 mediumProfile = generateProfile{76 ParentDegreeWeights: [5]int{10, 10, 12, 2, -4},77 OrthogonalPackedWeight: 6,78 DiagonalPackedWeight: 3,79 SpanGrowthWeight: 14,80 MinSpanRatio: 0.70,81 }82 hardProfile = generateProfile{83 ParentDegreeWeights: [5]int{6, 6, 16, 8, 0},84 OrthogonalPackedWeight: 8,85 DiagonalPackedWeight: 4,86 SpanGrowthWeight: 16,87 MinSpanRatio: 0.78,88 }89 expertProfile = generateProfile{90 ParentDegreeWeights: [5]int{4, 4, 18, 10, 2},91 OrthogonalPackedWeight: 10,92 DiagonalPackedWeight: 5,93 SpanGrowthWeight: 18,94 MinSpanRatio: 0.84,95 }96)9798var Modes = []game.Mode{99 NewMode("Mini 5x5", "Compact 5×5 network with a denser starter tree.", 5, 0.50, miniProfile),100 NewMode("Easy 7x7", "7×7 board with fuller coverage and gentle local tangles.", 7, 0.57, easyProfile),101 NewMode("Medium 9x9", "Balanced 9×9 network with tighter clusters and more branching.", 9, 0.64, mediumProfile),102 NewMode("Hard 11x11", "Dense 11×11 board that packs branches into close local interactions.", 11, 0.72, hardProfile),103 NewMode("Expert 13x13", "Large, crowded network with heavy branching and frequent near-miss tangles.", 13, 0.78, expertProfile),104}105106var ModeDefinitions = netwalkModeDefinitions(Modes)107108func netwalkModeDefinitions(modes []game.Mode) []puzzle.ModeDef {109 defs := gameentry.BuildModeDefs(modes)110 presets := []difficulty.Elo{400, 900, 1500, 2200, 2800}111 for i := range defs {112 if i >= len(presets) {113 break114 }115 elo := presets[i]116 defs[i].PresetElo = &elo117 }118 return defs119}120121var Definition = puzzle.NewDefinition(puzzle.DefinitionSpec{122 Name: "Netwalk",123 Description: "Rotate network tiles until every computer connects to the server.",124 Aliases: []string{"network"},125 Modes: ModeDefinitions,126 DailyModeIDs: puzzle.SelectModeIDsByIndex(ModeDefinitions, 1, 2),127})128129var Entry = gameentry.NewEntry(gameentry.EntrySpec{130 Definition: Definition,131 Help: HelpContent,132 Import: game.AdaptImport(ImportModel),133 Modes: Modes,134 Print: PDFPrintAdapter,135})