simulator.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package main
  2. import (
  3. "fmt"
  4. "slices"
  5. )
  6. type Simulation struct {
  7. Game *Game
  8. BestGuess string
  9. BestGuessRounds int
  10. SuccessCount int
  11. FailCount int
  12. TotalRounds int
  13. }
  14. func NewSimulator(g Game) *Simulation {
  15. return &Simulation{
  16. Game: &g,
  17. }
  18. }
  19. func (s Simulation) SimulateAllPossibleGames() (string, error) {
  20. bestGuessLossCount := 99
  21. bestGuessMaxRounds := 99
  22. bestGuessTotalRounds := 99
  23. wordLossCounts := make(map[string]int)
  24. totRoundCounts := make(map[string]int)
  25. maxRoundCounts := make(map[string]int)
  26. var words []string
  27. for word := range s.Game.Words {
  28. words = append(words, word)
  29. }
  30. slices.Sort(words)
  31. for _, initialWord := range words {
  32. fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  33. simulatedGame := NewGame()
  34. simulatedGame.Words = make(map[string]*Word)
  35. for word := range s.Game.Words {
  36. simulatedGame.Words[word] = &Word{Word: word}
  37. }
  38. lossCount, maxRounds, totalRounds := s.SimulateOneInitialWord(simulatedGame, initialWord)
  39. wordLossCounts[initialWord] = lossCount
  40. totRoundCounts[initialWord] = totalRounds
  41. maxRoundCounts[initialWord] = maxRounds
  42. //fmt.Printf("Initial Word: %s Loss Count: %d Max Rounds: %d Total Rounds: %d\n", initialWord, lossCount, maxRounds, totalRounds)
  43. if lossCount < bestGuessLossCount {
  44. bestGuessLossCount = lossCount
  45. bestGuessTotalRounds = totalRounds
  46. bestGuessMaxRounds = maxRounds
  47. } else if lossCount == bestGuessLossCount && maxRounds < bestGuessMaxRounds {
  48. bestGuessTotalRounds = totalRounds
  49. bestGuessMaxRounds = maxRounds
  50. } else if lossCount == bestGuessLossCount && maxRounds == bestGuessMaxRounds && totalRounds < bestGuessTotalRounds {
  51. bestGuessTotalRounds = totalRounds
  52. }
  53. }
  54. fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
  55. fmt.Print("Simulation completed\n\n")
  56. s.Game.scoreWordsByCommonLetterLocations()
  57. var bestGuess string
  58. for _, word := range s.Game.getSortedScores() {
  59. if wordLossCounts[word] == bestGuessLossCount && totRoundCounts[word] == bestGuessTotalRounds && maxRoundCounts[word] == bestGuessMaxRounds {
  60. fmt.Printf("Best Guess: %s Failed Branches: %d Total Rounds: %d Max Rounds: %d\n\n", word, bestGuessLossCount, bestGuessTotalRounds, bestGuessMaxRounds)
  61. bestGuess = word
  62. break
  63. }
  64. }
  65. return bestGuess, nil
  66. }
  67. func (s Simulation) SimulateOneInitialWord(game *Game, initialWord string) (lossCount, maxRounds, totalRounds int) {
  68. debugPrint("Initial Word: %s\n", initialWord)
  69. for answer := range s.Game.Words {
  70. debugPrint(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n")
  71. debugPrint("Target Word: %s\n", answer)
  72. simulatedGame := NewGame()
  73. simulatedGame.Words = make(map[string]*Word)
  74. for word := range game.Words {
  75. simulatedGame.Words[word] = &Word{Word: word}
  76. }
  77. won, rounds := s.SimulateOneGame(simulatedGame, initialWord, answer)
  78. if !won {
  79. lossCount++
  80. }
  81. totalRounds += rounds
  82. if rounds > maxRounds {
  83. maxRounds = rounds
  84. }
  85. }
  86. fmt.Printf("Summary: Initial Word: %s Loss Count: %d Max Rounds: %d Total Rounds: %d\n", initialWord, lossCount, maxRounds, totalRounds)
  87. return lossCount, maxRounds, totalRounds
  88. }
  89. func (s Simulation) SimulateOneGame(simulatedGame *Game, initialWord, answer string) (bool, int) {
  90. if initialWord == answer {
  91. fmt.Printf(" Win in 1 round => %s .. %s\n", initialWord, answer)
  92. return true, 1
  93. }
  94. guess := initialWord
  95. // 10 rounds max just to prevent infinite loops
  96. var round int
  97. for round = 1; round <= 10; round++ {
  98. if len(simulatedGame.Words) == 1 {
  99. debugPrint("Only one guess remaining after filtering: %s\n", guess)
  100. break
  101. }
  102. if round == 1 {
  103. debugPrint("First guess, initial Word: %s\n", initialWord)
  104. } else {
  105. guess = simulatedGame.getBestGuess()
  106. debugPrint("Round %d, guess: %s\n", round, guess)
  107. }
  108. if guess == answer {
  109. break
  110. }
  111. score := s.getScore(guess, answer)
  112. debugPrint("guess '%s' matches %d locations in answer '%s'\n", guess, score, answer)
  113. simulatedGame.FilterWords(guess, score)
  114. debugPrint("Words remaining after filtering: %+v\n", simulatedGame.Words)
  115. simulatedGame.scoreWordsByCommonLetterLocations()
  116. debugPrint("End round %d, not solved, %d words remaining\n", round, len(simulatedGame.Words))
  117. }
  118. if round > 4 {
  119. fmt.Printf(" Loss in %d rounds => %s .. %s\n", round, initialWord, answer)
  120. return false, round
  121. }
  122. fmt.Printf(" Win in %d rounds => %s .. %s\n", round, initialWord, answer)
  123. return true, round
  124. }
  125. func (s Simulation) getScore(guess string, answer string) int {
  126. score := 0
  127. for idx, letter := range answer {
  128. if string(letter) == string(guess[idx]) {
  129. score++
  130. }
  131. }
  132. return score
  133. }