mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 23:26:40 -08:00
Unfixes the fix introduced in a previous commit regarding how the computer deduces the answer. ReadMe updated with a thorough explanation of the deduction process.
This commit is contained in:
@@ -28,20 +28,126 @@ As published in Basic Computer Games (1978):
|
|||||||
Downloaded from Vintage Basic at
|
Downloaded from Vintage Basic at
|
||||||
http://www.vintage-basic.net/games.html
|
http://www.vintage-basic.net/games.html
|
||||||
|
|
||||||
#### Porting Notes
|
###How the computer deduces your guess.
|
||||||
|
|
||||||
in [#613](https://github.com/coding-horror/basic-computer-games/pull/613)
|
The computer takes the number of black pegs and white pegs that the user reports
|
||||||
|
and uses that information as a target. It then assumes its guess is the answer
|
||||||
|
and proceeds to compare the black and white pegs against all remaining possible
|
||||||
|
answers. For each set of black and white pegs it gets in these comparisons, if
|
||||||
|
they don't match what the user reported, then they can not be part of the solution.
|
||||||
|
This can be a non-intuitive assumption, so we'll walk through it with a three color,
|
||||||
|
three position example (27 possible solutions.)
|
||||||
|
|
||||||
|
Let's just suppose our secret code we're hiding from the computer is `BWB`
|
||||||
|
|
||||||
|
First let's point out the commutative property of comparing two codes for their
|
||||||
|
black and white pegs. A black peg meaning correct color and correct position, and
|
||||||
|
a white peg meaning correct color and wrong position. If the computer guesses
|
||||||
|
`RBW` then the black/white peg report is 0 black, 2 white. But if `RBW` is the
|
||||||
|
secret code and the computer guesses `BWB` the reporting for `BWB` is going to be
|
||||||
|
the same, 0 black, 2 white.
|
||||||
|
|
||||||
|
Now lets look at a table with the reporting for every possible guess the computer
|
||||||
|
can make while our secret code is `BWB`.
|
||||||
|
|
||||||
|
| Guess | Black | White | | Guess | Black | White | | Guess | Black | White |
|
||||||
|
|-------|-------|-------|-----|-------|-------|-------|-----|-------|-------|-------|
|
||||||
|
| BBB | 2 | 0 | | WBB | 1 | 2 | | RBB | 1 | 1 |
|
||||||
|
| BBW | 1 | 2 | | WBW | 0 | 2 | | RBW | 0 | 2 |
|
||||||
|
| BBR | 1 | 1 | | WBR | 0 | 2 | | RBR | 0 | 1 |
|
||||||
|
| BWB | 3 | 0 | | WWB | 2 | 0 | | RWB | 2 | 0 |
|
||||||
|
| BWW | 2 | 0 | | WWW | 1 | 0 | | RWW | 1 | 0 |
|
||||||
|
| BWR | 2 | 0 | | WWR | 1 | 0 | | RWR | 1 | 0 |
|
||||||
|
| BRB | 2 | 0 | | WRB | 1 | 1 | | RRB | 1 | 0 |
|
||||||
|
| BRW | 1 | 1 | | WRW | 0 | 1 | | RRW | 0 | 1 |
|
||||||
|
| BRR | 1 | 0 | | WRR | 0 | 1 | | RRR | 0 | 0 |
|
||||||
|
|
||||||
|
The computer has guessed `RBW` and the report on it is 0 black, 2 white. The code
|
||||||
|
used to eliminate other solutions looks like this:
|
||||||
|
|
||||||
`1060 IF B1<>B OR W1<>W THEN I(X)=0`
|
`1060 IF B1<>B OR W1<>W THEN I(X)=0`
|
||||||
|
|
||||||
was changed to:
|
which says set `RBW` as the secret and compare it to all remaining solutions and
|
||||||
|
get rid of any that don't match the same black and white report, 0 black and 2 white.
|
||||||
|
So let's do that.
|
||||||
|
|
||||||
`1060 IF B1>B OR W1>W THEN I(X)=0`
|
Remember, `RBW` is pretending to be the secret code here. These are the remaining
|
||||||
|
solutions reporting their black and white pegs against `RBW`.
|
||||||
|
|
||||||
This was done because of a bug:
|
| Guess | Black | White | | Guess | Black | White | | Guess | Black | White |
|
||||||
|
|-------|-------|-------|-----|-------|-------|-------|-----|-------|-------|-------|
|
||||||
|
| BBB | 1 | 0 | | WBB | 1 | 1 | | RBB | 2 | 0 |
|
||||||
|
| BBW | 2 | 0 | | WBW | 2 | 0 | | RBW | 3 | 0 |
|
||||||
|
| BBR | 1 | 1 | | WBR | 1 | 2 | | RBR | 2 | 0 |
|
||||||
|
| BWB | 0 | 2 | | WWB | 0 | 2 | | RWB | 1 | 2 |
|
||||||
|
| BWW | 1 | 1 | | WWW | 1 | 0 | | RWW | 2 | 0 |
|
||||||
|
| BWR | 0 | 3 | | WWR | 1 | 1 | | RWR | 1 | 1 |
|
||||||
|
| BRB | 0 | 2 | | WRB | 0 | 3 | | RRB | 1 | 1 |
|
||||||
|
| BRW | 1 | 2 | | WRW | 1 | 1 | | RRW | 2 | 0 |
|
||||||
|
| BRR | 0 | 2 | | WRR | 0 | 2 | | RRR | 1 | 0 |
|
||||||
|
|
||||||
Originally, after guessing and getting feedback, the computer would look through every possible combination, and for all that haven't previously been marked as impossible it would check whether or not the black and white pins that that combination should get are not-equal to what its previous guess got and, if they are equal, the combination would be marked as possible, and if they aren't equal then the combination would be marked as impossible. This results in a bug where the computer eliminates the correct answer as a possible solution after the first guess, unless the first guess just happens to be correct.
|
Now we are going to eliminate every solution that **DOESN'T** matches 0 black and 2 white.
|
||||||
|
|
||||||
this was discussed in more detail in [issue #611](https://github.com/coding-horror/basic-computer-games/issues/611)
|
| Guess | Black | White | | Guess | Black | White | | Guess | Black | White |
|
||||||
|
|----------|-------|-------|-----|----------|-------|-------|-----|----------|-------|-------|
|
||||||
|
| ~~~BBB~~ | 1 | 0 | | ~~~WBB~~ | 1 | 1 | | ~~~RBB~~ | 2 | 0 |
|
||||||
|
| ~~~BBW~~ | 2 | 0 | | ~~~WBW~~ | 2 | 0 | | ~~~RBW~~ | 3 | 0 |
|
||||||
|
| ~~~BBR~~ | 1 | 1 | | ~~~WBR~~ | 1 | 2 | | ~~~RBR~~ | 2 | 0 |
|
||||||
|
| BWB | 0 | 2 | | WWB | 0 | 2 | | ~~~RWB~~ | 1 | 2 |
|
||||||
|
| ~~~BWW~~ | 1 | 1 | | ~~~WWW~~ | 1 | 0 | | ~~~RWW~~ | 2 | 0 |
|
||||||
|
| ~~~BWR~~ | 0 | 3 | | ~~~WWR~~ | 1 | 1 | | ~~~RWR~~ | 1 | 1 |
|
||||||
|
| BRB | 0 | 2 | | ~~~WRB~~ | 0 | 3 | | ~~~RRB~~ | 1 | 1 |
|
||||||
|
| ~~~BRW~~ | 1 | 2 | | ~~~WRW~~ | 1 | 1 | | ~~~RRW~~ | 2 | 0 |
|
||||||
|
| BRR | 0 | 2 | | WRR | 0 | 2 | | ~~~RRR~~ | 1 | 0 |
|
||||||
|
|
||||||
|
That wipes out all but five solutions. Notice how the entire right column of solutions
|
||||||
|
is eliminated, including our original guess of `RBW`, therefore eliminating any
|
||||||
|
special case to specifically eliminate this guess from the solution set when we first find out
|
||||||
|
its not the answer.
|
||||||
|
|
||||||
|
Continuing on, we have the following solutions left of which our secret code, `BWB`
|
||||||
|
is one of them. Remember our commutative property explained previously.
|
||||||
|
|
||||||
additionally, it's recommended that you have the computer elimate it's previous guess as possible unless that guess was correct. (the rust port does this)
|
| Guess | Black | White |
|
||||||
|
|-------|-------|-------|
|
||||||
|
| BWB | 0 | 2 |
|
||||||
|
| BRB | 0 | 2 |
|
||||||
|
| BRR | 0 | 2 |
|
||||||
|
| WWB | 0 | 2 |
|
||||||
|
| WRR | 0 | 2 |
|
||||||
|
|
||||||
|
So for its second pick, the computer will randomly pick one of these remaining solutions. Let's pick
|
||||||
|
the middle one, `BRR`, and perform the same ritual. Our user reports to the computer
|
||||||
|
that it now has 1 black, 0 whites when comparing to our secret code `BWB`. Let's
|
||||||
|
now compare `BRR` to the remaining five solutions and eliminate any that **DON'T**
|
||||||
|
report 1 black and 0 whites.
|
||||||
|
|
||||||
|
| Guess | Black | White |
|
||||||
|
|----------|-------|-------|
|
||||||
|
| BWB | 1 | 0 |
|
||||||
|
| ~~~BRB~~ | 2 | 0 |
|
||||||
|
| ~~~BRR~~ | 3 | 0 |
|
||||||
|
| ~~~WWB~~ | 0 | 1 |
|
||||||
|
| ~~~WRR~~ | 2 | 0 |
|
||||||
|
|
||||||
|
Only one solution matches and its our secret code! The computer will guess this
|
||||||
|
one next as it's the only choice left, for a total of three moves.
|
||||||
|
Coincidentally, I believe the expected maximum number of moves the computer will
|
||||||
|
make is the number of positions plus one for the initial guess with no information.
|
||||||
|
This is because it is winnowing down the solutions
|
||||||
|
logarithmically on average. You noticed on the first pass, it wiped out 22
|
||||||
|
solutions. If it was doing this logarithmically the worst case guess would
|
||||||
|
still eliminate 18 of the solutions leaving 9 (3<sup>2</sup>). So we have as
|
||||||
|
a guideline:
|
||||||
|
|
||||||
|
Log<sub>(# of Colors)</sub>TotalPossibilities
|
||||||
|
|
||||||
|
but TotalPossibilities = (# of Colors)<sup># of Positions</sup>
|
||||||
|
|
||||||
|
so you end up with the number of positions as a guess limit. If you consider the
|
||||||
|
simplest non-trivial puzzle, two colors with two positions, and you guess BW or
|
||||||
|
WB first, the most you can logically deduce if you get 1 black and 1 white is
|
||||||
|
that it is either WW, or BB which could bring your total guesses up to three
|
||||||
|
which is the number of positions plus one. So if your computer's turn is taking
|
||||||
|
longer than the number of positions plus one to find the answer then something
|
||||||
|
is wrong with your code.
|
||||||
@@ -152,7 +152,7 @@ namespace Game
|
|||||||
if (isCandidate[index])
|
if (isCandidate[index])
|
||||||
{
|
{
|
||||||
var (candidateBlacks, candidateWhites) = guess.Compare(candidate);
|
var (candidateBlacks, candidateWhites) = guess.Compare(candidate);
|
||||||
if (blacks > candidateBlacks || whites > candidateWhites)
|
if (blacks != candidateBlacks || whites != candidateWhites)
|
||||||
isCandidate[index] = false;
|
isCandidate[index] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ async function main()
|
|||||||
copy_hs();
|
copy_hs();
|
||||||
convert_qa();
|
convert_qa();
|
||||||
get_number();
|
get_number();
|
||||||
if (b1 > b || w1 > w)
|
if (b1 != b || w1 != w)
|
||||||
ia[x] = 0;
|
ia[x] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@
|
|||||||
1035 GOSUB 6500
|
1035 GOSUB 6500
|
||||||
1040 GOSUB 4000
|
1040 GOSUB 4000
|
||||||
1050 GOSUB 4500
|
1050 GOSUB 4500
|
||||||
1060 IF B1>B OR W1>W THEN I(X)=0
|
1060 IF B1<>B OR W1<>W THEN I(X)=0
|
||||||
1070 NEXT X
|
1070 NEXT X
|
||||||
1080 NEXT M
|
1080 NEXT M
|
||||||
1090 PRINT "I USED UP ALL MY MOVES!"
|
1090 PRINT "I USED UP ALL MY MOVES!"
|
||||||
|
|||||||
@@ -1,271 +1,271 @@
|
|||||||
import random
|
import random
|
||||||
import sys
|
import sys
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
|
|
||||||
# Global variables
|
# Global variables
|
||||||
colors = ["BLACK", "WHITE", "RED", "GREEN", "ORANGE", "YELLOW", "PURPLE", "TAN"]
|
colors = ["BLACK", "WHITE", "RED", "GREEN", "ORANGE", "YELLOW", "PURPLE", "TAN"]
|
||||||
color_letters = "BWRGOYPT"
|
color_letters = "BWRGOYPT"
|
||||||
num_positions = 0
|
num_positions = 0
|
||||||
num_colors = 100
|
num_colors = 100
|
||||||
human_score = 0
|
human_score = 0
|
||||||
computer_score = 0
|
computer_score = 0
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
global colors, color_letters, num_positions, num_colors, human_score, computer_score
|
global colors, color_letters, num_positions, num_colors, human_score, computer_score
|
||||||
colors = ["BLACK", "WHITE", "RED", "GREEN", "ORANGE", "YELLOW", "PURPLE", "TAN"]
|
colors = ["BLACK", "WHITE", "RED", "GREEN", "ORANGE", "YELLOW", "PURPLE", "TAN"]
|
||||||
color_letters = "BWRGOYPT"
|
color_letters = "BWRGOYPT"
|
||||||
|
|
||||||
num_colors = 100
|
num_colors = 100
|
||||||
human_score = 0
|
human_score = 0
|
||||||
computer_score = 0
|
computer_score = 0
|
||||||
|
|
||||||
# get user inputs for game conditions
|
# get user inputs for game conditions
|
||||||
print("Mastermind")
|
print("Mastermind")
|
||||||
print("Creative Computing Morristown, New Jersey")
|
print("Creative Computing Morristown, New Jersey")
|
||||||
while num_colors > 8:
|
while num_colors > 8:
|
||||||
num_colors = int(input("Number of colors (max 8): ")) # C9 in BASIC
|
num_colors = int(input("Number of colors (max 8): ")) # C9 in BASIC
|
||||||
num_positions = int(input("Number of positions: ")) # P9 in BASIC
|
num_positions = int(input("Number of positions: ")) # P9 in BASIC
|
||||||
num_rounds = int(input("Number of rounds: ")) # R9 in BASIC
|
num_rounds = int(input("Number of rounds: ")) # R9 in BASIC
|
||||||
possibilities = num_colors**num_positions
|
possibilities = num_colors**num_positions
|
||||||
all_possibilities = [1] * possibilities
|
all_possibilities = [1] * possibilities
|
||||||
|
|
||||||
print(f"Number of possibilities {possibilities}")
|
print(f"Number of possibilities {possibilities}")
|
||||||
print("Color\tLetter")
|
print("Color\tLetter")
|
||||||
print("=====\t======")
|
print("=====\t======")
|
||||||
for element in range(0, num_colors):
|
for element in range(0, num_colors):
|
||||||
print(f"{colors[element]}\t{colors[element][0]}")
|
print(f"{colors[element]}\t{colors[element][0]}")
|
||||||
|
|
||||||
current_round = 1
|
current_round = 1
|
||||||
|
|
||||||
while current_round <= num_rounds:
|
while current_round <= num_rounds:
|
||||||
print(f"Round number {current_round}")
|
print(f"Round number {current_round}")
|
||||||
num_moves = 1
|
num_moves = 1
|
||||||
guesses: List[List[Union[str, int]]] = []
|
guesses: List[List[Union[str, int]]] = []
|
||||||
turn_over = False
|
turn_over = False
|
||||||
print("Guess my combination ...")
|
print("Guess my combination ...")
|
||||||
answer = int(possibilities * random.random())
|
answer = int(possibilities * random.random())
|
||||||
numeric_answer = [-1] * num_positions
|
numeric_answer = [-1] * num_positions
|
||||||
for _ in range(0, answer):
|
for _ in range(0, answer):
|
||||||
numeric_answer = get_possibility(numeric_answer)
|
numeric_answer = get_possibility(numeric_answer)
|
||||||
# human_readable_answer = make_human_readable(numeric_answer, color_letters)
|
# human_readable_answer = make_human_readable(numeric_answer, color_letters)
|
||||||
while num_moves < 10 and not turn_over:
|
while num_moves < 10 and not turn_over:
|
||||||
print(f"Move # {num_moves} Guess : ")
|
print(f"Move # {num_moves} Guess : ")
|
||||||
user_command = input("Guess ")
|
user_command = input("Guess ")
|
||||||
if user_command == "BOARD":
|
if user_command == "BOARD":
|
||||||
print_board(guesses) # 2000
|
print_board(guesses) # 2000
|
||||||
elif user_command == "QUIT": # 2500
|
elif user_command == "QUIT": # 2500
|
||||||
human_readable_answer = make_human_readable(
|
human_readable_answer = make_human_readable(
|
||||||
numeric_answer, color_letters
|
numeric_answer, color_letters
|
||||||
)
|
)
|
||||||
print(f"QUITTER! MY COMBINATION WAS: {human_readable_answer}")
|
print(f"QUITTER! MY COMBINATION WAS: {human_readable_answer}")
|
||||||
print("GOOD BYE")
|
print("GOOD BYE")
|
||||||
quit()
|
quit()
|
||||||
elif len(user_command) != num_positions: # 410
|
elif len(user_command) != num_positions: # 410
|
||||||
print("BAD NUMBER OF POSITIONS")
|
print("BAD NUMBER OF POSITIONS")
|
||||||
else:
|
else:
|
||||||
invalid_letters = get_invalid_letters(user_command)
|
invalid_letters = get_invalid_letters(user_command)
|
||||||
if invalid_letters > "":
|
if invalid_letters > "":
|
||||||
print(f"INVALID GUESS: {invalid_letters}")
|
print(f"INVALID GUESS: {invalid_letters}")
|
||||||
else:
|
else:
|
||||||
guess_results = compare_two_positions(
|
guess_results = compare_two_positions(
|
||||||
user_command, make_human_readable(numeric_answer, color_letters)
|
user_command, make_human_readable(numeric_answer, color_letters)
|
||||||
)
|
)
|
||||||
print(f"Results: {guess_results}")
|
print(f"Results: {guess_results}")
|
||||||
if guess_results[1] == num_positions: # correct guess
|
if guess_results[1] == num_positions: # correct guess
|
||||||
turn_over = True
|
turn_over = True
|
||||||
print(f"You guessed it in {num_moves} moves!")
|
print(f"You guessed it in {num_moves} moves!")
|
||||||
human_score = human_score + num_moves
|
human_score = human_score + num_moves
|
||||||
print_score(computer_score, human_score)
|
print_score(computer_score, human_score)
|
||||||
else:
|
else:
|
||||||
print(
|
print(
|
||||||
"You have {} blacks and {} whites".format(
|
"You have {} blacks and {} whites".format(
|
||||||
guess_results[1], guess_results[2]
|
guess_results[1], guess_results[2]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
num_moves = num_moves + 1
|
num_moves = num_moves + 1
|
||||||
guesses.append(guess_results)
|
guesses.append(guess_results)
|
||||||
if not turn_over: # RAN OUT OF MOVES
|
if not turn_over: # RAN OUT OF MOVES
|
||||||
print("YOU RAN OUT OF MOVES! THAT'S ALL YOU GET!")
|
print("YOU RAN OUT OF MOVES! THAT'S ALL YOU GET!")
|
||||||
print(
|
print(
|
||||||
"THE ACTUAL COMBINATION WAS: {}".format(
|
"THE ACTUAL COMBINATION WAS: {}".format(
|
||||||
make_human_readable(numeric_answer, color_letters)
|
make_human_readable(numeric_answer, color_letters)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
human_score = human_score + num_moves
|
human_score = human_score + num_moves
|
||||||
print_score(computer_score, human_score)
|
print_score(computer_score, human_score)
|
||||||
|
|
||||||
# COMPUTER TURN
|
# COMPUTER TURN
|
||||||
guesses = []
|
guesses = []
|
||||||
turn_over = False
|
turn_over = False
|
||||||
inconsistent_information = False
|
inconsistent_information = False
|
||||||
while not turn_over and not inconsistent_information:
|
while not turn_over and not inconsistent_information:
|
||||||
all_possibilities = [1] * possibilities
|
all_possibilities = [1] * possibilities
|
||||||
num_moves = 1
|
num_moves = 1
|
||||||
inconsistent_information = False
|
inconsistent_information = False
|
||||||
print("NOW I GUESS. THINK OF A COMBINATION.")
|
print("NOW I GUESS. THINK OF A COMBINATION.")
|
||||||
input("HIT RETURN WHEN READY: ")
|
input("HIT RETURN WHEN READY: ")
|
||||||
while num_moves < 10 and not turn_over and not inconsistent_information:
|
while num_moves < 10 and not turn_over and not inconsistent_information:
|
||||||
found_guess = False
|
found_guess = False
|
||||||
computer_guess = int(possibilities * random.random())
|
computer_guess = int(possibilities * random.random())
|
||||||
if (
|
if (
|
||||||
all_possibilities[computer_guess] == 1
|
all_possibilities[computer_guess] == 1
|
||||||
): # random guess is possible, use it
|
): # random guess is possible, use it
|
||||||
found_guess = True
|
found_guess = True
|
||||||
guess = computer_guess
|
guess = computer_guess
|
||||||
else:
|
else:
|
||||||
for i in range(computer_guess, possibilities):
|
for i in range(computer_guess, possibilities):
|
||||||
if all_possibilities[i] == 1:
|
if all_possibilities[i] == 1:
|
||||||
found_guess = True
|
found_guess = True
|
||||||
guess = i
|
guess = i
|
||||||
break
|
break
|
||||||
if not found_guess:
|
if not found_guess:
|
||||||
for i in range(0, computer_guess):
|
for i in range(0, computer_guess):
|
||||||
if all_possibilities[i] == 1:
|
if all_possibilities[i] == 1:
|
||||||
found_guess = True
|
found_guess = True
|
||||||
guess = i
|
guess = i
|
||||||
break
|
break
|
||||||
if not found_guess: # inconsistent info from user
|
if not found_guess: # inconsistent info from user
|
||||||
print("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.")
|
print("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.")
|
||||||
print("TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.")
|
print("TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.")
|
||||||
turn_over = True
|
turn_over = True
|
||||||
inconsistent_information = True
|
inconsistent_information = True
|
||||||
else:
|
else:
|
||||||
numeric_guess = [-1] * num_positions
|
numeric_guess = [-1] * num_positions
|
||||||
for _ in range(0, guess):
|
for _ in range(0, guess):
|
||||||
numeric_guess = get_possibility(numeric_guess)
|
numeric_guess = get_possibility(numeric_guess)
|
||||||
human_readable_guess = make_human_readable(
|
human_readable_guess = make_human_readable(
|
||||||
numeric_guess, color_letters
|
numeric_guess, color_letters
|
||||||
)
|
)
|
||||||
print(f"My guess is: {human_readable_guess}")
|
print(f"My guess is: {human_readable_guess}")
|
||||||
blacks_str, whites_str = input(
|
blacks_str, whites_str = input(
|
||||||
"ENTER BLACKS, WHITES (e.g. 1,2): "
|
"ENTER BLACKS, WHITES (e.g. 1,2): "
|
||||||
).split(",")
|
).split(",")
|
||||||
blacks = int(blacks_str)
|
blacks = int(blacks_str)
|
||||||
whites = int(whites_str)
|
whites = int(whites_str)
|
||||||
if blacks == num_positions: # Correct guess
|
if blacks == num_positions: # Correct guess
|
||||||
print(f"I GOT IT IN {num_moves} MOVES")
|
print(f"I GOT IT IN {num_moves} MOVES")
|
||||||
turn_over = True
|
turn_over = True
|
||||||
computer_score = computer_score + num_moves
|
computer_score = computer_score + num_moves
|
||||||
print_score(computer_score, human_score)
|
print_score(computer_score, human_score)
|
||||||
else:
|
else:
|
||||||
num_moves += 1
|
num_moves += 1
|
||||||
for i in range(0, possibilities):
|
for i in range(0, possibilities):
|
||||||
if all_possibilities[i] == 0: # already ruled out
|
if all_possibilities[i] == 0: # already ruled out
|
||||||
continue
|
continue
|
||||||
numeric_possibility = [-1] * num_positions
|
numeric_possibility = [-1] * num_positions
|
||||||
for _ in range(0, i):
|
for _ in range(0, i):
|
||||||
numeric_possibility = get_possibility(
|
numeric_possibility = get_possibility(
|
||||||
numeric_possibility
|
numeric_possibility
|
||||||
)
|
)
|
||||||
human_readable_possibility = make_human_readable(
|
human_readable_possibility = make_human_readable(
|
||||||
numeric_possibility, color_letters
|
numeric_possibility, color_letters
|
||||||
) # 4000
|
) # 4000
|
||||||
comparison = compare_two_positions(
|
comparison = compare_two_positions(
|
||||||
human_readable_possibility, human_readable_guess
|
human_readable_possibility, human_readable_guess
|
||||||
)
|
)
|
||||||
print(comparison)
|
print(comparison)
|
||||||
if (blacks > comparison[1]) or (whites > comparison[2]): # type: ignore
|
if ((blacks != comparison[1]) or (whites != comparison[2])): # type: ignore
|
||||||
all_possibilities[i] = 0
|
all_possibilities[i] = 0
|
||||||
if not turn_over: # COMPUTER DID NOT GUESS
|
if not turn_over: # COMPUTER DID NOT GUESS
|
||||||
print("I USED UP ALL MY MOVES!")
|
print("I USED UP ALL MY MOVES!")
|
||||||
print("I GUESS MY CPU IS JUST HAVING AN OFF DAY.")
|
print("I GUESS MY CPU IS JUST HAVING AN OFF DAY.")
|
||||||
computer_score = computer_score + num_moves
|
computer_score = computer_score + num_moves
|
||||||
print_score(computer_score, human_score)
|
print_score(computer_score, human_score)
|
||||||
current_round += 1
|
current_round += 1
|
||||||
print_score(computer_score, human_score, is_final_score=True)
|
print_score(computer_score, human_score, is_final_score=True)
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
# 470
|
# 470
|
||||||
def get_invalid_letters(user_command) -> str:
|
def get_invalid_letters(user_command) -> str:
|
||||||
"""Makes sure player input consists of valid colors for selected game configuration."""
|
"""Makes sure player input consists of valid colors for selected game configuration."""
|
||||||
valid_colors = color_letters[:num_colors]
|
valid_colors = color_letters[:num_colors]
|
||||||
invalid_letters = ""
|
invalid_letters = ""
|
||||||
for letter in user_command:
|
for letter in user_command:
|
||||||
if letter not in valid_colors:
|
if letter not in valid_colors:
|
||||||
invalid_letters = invalid_letters + letter
|
invalid_letters = invalid_letters + letter
|
||||||
return invalid_letters
|
return invalid_letters
|
||||||
|
|
||||||
|
|
||||||
# 2000
|
# 2000
|
||||||
def print_board(guesses) -> None:
|
def print_board(guesses) -> None:
|
||||||
"""Print previous guesses within the round."""
|
"""Print previous guesses within the round."""
|
||||||
print("Board")
|
print("Board")
|
||||||
print("Move\tGuess\tBlack White")
|
print("Move\tGuess\tBlack White")
|
||||||
for idx, guess in enumerate(guesses):
|
for idx, guess in enumerate(guesses):
|
||||||
print(f"{idx + 1}\t{guess[0]}\t{guess[1]} {guess[2]}")
|
print(f"{idx + 1}\t{guess[0]}\t{guess[1]} {guess[2]}")
|
||||||
|
|
||||||
|
|
||||||
# 3500
|
# 3500
|
||||||
# Easily the place for most optimization, since they generate every possibility
|
# Easily the place for most optimization, since they generate every possibility
|
||||||
# every time when checking for potential solutions
|
# every time when checking for potential solutions
|
||||||
# From the original article:
|
# From the original article:
|
||||||
# "We did try a version that kept an actual list of all possible combinations
|
# "We did try a version that kept an actual list of all possible combinations
|
||||||
# (as a string array), which was significantly faster than this versionn but
|
# (as a string array), which was significantly faster than this versionn but
|
||||||
# which ate tremendous amounts of memory."
|
# which ate tremendous amounts of memory."
|
||||||
def get_possibility(possibility) -> List[int]:
|
def get_possibility(possibility) -> List[int]:
|
||||||
# print(possibility)
|
# print(possibility)
|
||||||
if possibility[0] > -1: # 3530
|
if possibility[0] > -1: # 3530
|
||||||
current_position = 0 # Python arrays are zero-indexed
|
current_position = 0 # Python arrays are zero-indexed
|
||||||
while True:
|
while True:
|
||||||
if possibility[current_position] < num_colors - 1: # zero-index again
|
if possibility[current_position] < num_colors - 1: # zero-index again
|
||||||
possibility[current_position] += 1
|
possibility[current_position] += 1
|
||||||
return possibility
|
return possibility
|
||||||
else:
|
else:
|
||||||
possibility[current_position] = 0
|
possibility[current_position] = 0
|
||||||
current_position += 1
|
current_position += 1
|
||||||
else: # 3524
|
else: # 3524
|
||||||
possibility = [0] * num_positions
|
possibility = [0] * num_positions
|
||||||
return possibility
|
return possibility
|
||||||
|
|
||||||
|
|
||||||
# 4500
|
# 4500
|
||||||
def compare_two_positions(guess: str, answer: str) -> List[Union[str, int]]:
|
def compare_two_positions(guess: str, answer: str) -> List[Union[str, int]]:
|
||||||
"""Returns blacks (correct color and position) and whites (correct color only) for candidate position (guess) versus reference position (answer)."""
|
"""Returns blacks (correct color and position) and whites (correct color only) for candidate position (guess) versus reference position (answer)."""
|
||||||
increment = 0
|
increment = 0
|
||||||
blacks = 0
|
blacks = 0
|
||||||
whites = 0
|
whites = 0
|
||||||
initial_guess = guess
|
initial_guess = guess
|
||||||
for pos in range(0, num_positions):
|
for pos in range(0, num_positions):
|
||||||
if guess[pos] != answer[pos]:
|
if guess[pos] != answer[pos]:
|
||||||
for pos2 in range(0, num_positions):
|
for pos2 in range(0, num_positions):
|
||||||
if not (
|
if not (
|
||||||
guess[pos] != answer[pos2] or guess[pos2] == answer[pos2]
|
guess[pos] != answer[pos2] or guess[pos2] == answer[pos2]
|
||||||
): # correct color but not correct place
|
): # correct color but not correct place
|
||||||
whites = whites + 1
|
whites = whites + 1
|
||||||
answer = answer[:pos2] + chr(increment) + answer[pos2 + 1 :]
|
answer = answer[:pos2] + chr(increment) + answer[pos2 + 1 :]
|
||||||
guess = guess[:pos] + chr(increment + 1) + guess[pos + 1 :]
|
guess = guess[:pos] + chr(increment + 1) + guess[pos + 1 :]
|
||||||
increment = increment + 2
|
increment = increment + 2
|
||||||
else: # correct color and placement
|
else: # correct color and placement
|
||||||
blacks = blacks + 1
|
blacks = blacks + 1
|
||||||
# THIS IS DEVIOUSLY CLEVER
|
# THIS IS DEVIOUSLY CLEVER
|
||||||
guess = guess[:pos] + chr(increment + 1) + guess[pos + 1 :]
|
guess = guess[:pos] + chr(increment + 1) + guess[pos + 1 :]
|
||||||
answer = answer[:pos] + chr(increment) + answer[pos + 1 :]
|
answer = answer[:pos] + chr(increment) + answer[pos + 1 :]
|
||||||
increment = increment + 2
|
increment = increment + 2
|
||||||
return [initial_guess, blacks, whites]
|
return [initial_guess, blacks, whites]
|
||||||
|
|
||||||
|
|
||||||
# 5000 + logic from 1160
|
# 5000 + logic from 1160
|
||||||
def print_score(computer_score, human_score, is_final_score: bool = False) -> None:
|
def print_score(computer_score, human_score, is_final_score: bool = False) -> None:
|
||||||
"""Print score after each turn ends, including final score at end of game."""
|
"""Print score after each turn ends, including final score at end of game."""
|
||||||
if is_final_score:
|
if is_final_score:
|
||||||
print("GAME OVER")
|
print("GAME OVER")
|
||||||
print("FINAL SCORE:")
|
print("FINAL SCORE:")
|
||||||
else:
|
else:
|
||||||
print("SCORE:")
|
print("SCORE:")
|
||||||
print(f" COMPUTER {computer_score}")
|
print(f" COMPUTER {computer_score}")
|
||||||
print(f" HUMAN {human_score}")
|
print(f" HUMAN {human_score}")
|
||||||
|
|
||||||
|
|
||||||
# 4000, 5500, 6000 subroutines are all identical
|
# 4000, 5500, 6000 subroutines are all identical
|
||||||
def make_human_readable(num: List[int], color_letters) -> str:
|
def make_human_readable(num: List[int], color_letters) -> str:
|
||||||
"""Make the numeric representation of a position human readable."""
|
"""Make the numeric representation of a position human readable."""
|
||||||
retval = ""
|
retval = ""
|
||||||
for i in range(0, len(num)):
|
for i in range(0, len(num)):
|
||||||
retval = retval + color_letters[int(num[i])]
|
retval = retval + color_letters[int(num[i])]
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ fn main() {
|
|||||||
if *b.1 { //filter out ones we already know aren't possible
|
if *b.1 { //filter out ones we already know aren't possible
|
||||||
let mut tmp_guess = GUESS::new(CODE::new_from_int(b.0, num_colors, num_positions));
|
let mut tmp_guess = GUESS::new(CODE::new_from_int(b.0, num_colors, num_positions));
|
||||||
tmp_guess.evaluate(&answer);
|
tmp_guess.evaluate(&answer);
|
||||||
if blacks > tmp_guess.blacks || whites > tmp_guess.whites { //if number of blacks/whites is different, set it to false
|
if blacks != tmp_guess.blacks || whites != tmp_guess.whites { //if number of blacks/whites is different, set it to false
|
||||||
*b.1 = false;
|
*b.1 = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user