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:
Joe Nellis
2022-04-08 21:59:14 -07:00
parent 603efe0d7d
commit 5ff1717a53
6 changed files with 389 additions and 283 deletions

View File

@@ -28,20 +28,126 @@ As published in Basic Computer Games (1978):
Downloaded from Vintage Basic at
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`
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.

View File

@@ -152,7 +152,7 @@ namespace Game
if (isCandidate[index])
{
var (candidateBlacks, candidateWhites) = guess.Compare(candidate);
if (blacks > candidateBlacks || whites > candidateWhites)
if (blacks != candidateBlacks || whites != candidateWhites)
isCandidate[index] = false;
}
}

View File

@@ -343,7 +343,7 @@ async function main()
copy_hs();
convert_qa();
get_number();
if (b1 > b || w1 > w)
if (b1 != b || w1 != w)
ia[x] = 0;
}
}

View File

@@ -113,7 +113,7 @@
1035 GOSUB 6500
1040 GOSUB 4000
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
1080 NEXT M
1090 PRINT "I USED UP ALL MY MOVES!"

View File

@@ -1,271 +1,271 @@
import random
import sys
from typing import List, Union
# Global variables
colors = ["BLACK", "WHITE", "RED", "GREEN", "ORANGE", "YELLOW", "PURPLE", "TAN"]
color_letters = "BWRGOYPT"
num_positions = 0
num_colors = 100
human_score = 0
computer_score = 0
def main() -> None:
global colors, color_letters, num_positions, num_colors, human_score, computer_score
colors = ["BLACK", "WHITE", "RED", "GREEN", "ORANGE", "YELLOW", "PURPLE", "TAN"]
color_letters = "BWRGOYPT"
num_colors = 100
human_score = 0
computer_score = 0
# get user inputs for game conditions
print("Mastermind")
print("Creative Computing Morristown, New Jersey")
while num_colors > 8:
num_colors = int(input("Number of colors (max 8): ")) # C9 in BASIC
num_positions = int(input("Number of positions: ")) # P9 in BASIC
num_rounds = int(input("Number of rounds: ")) # R9 in BASIC
possibilities = num_colors**num_positions
all_possibilities = [1] * possibilities
print(f"Number of possibilities {possibilities}")
print("Color\tLetter")
print("=====\t======")
for element in range(0, num_colors):
print(f"{colors[element]}\t{colors[element][0]}")
current_round = 1
while current_round <= num_rounds:
print(f"Round number {current_round}")
num_moves = 1
guesses: List[List[Union[str, int]]] = []
turn_over = False
print("Guess my combination ...")
answer = int(possibilities * random.random())
numeric_answer = [-1] * num_positions
for _ in range(0, answer):
numeric_answer = get_possibility(numeric_answer)
# human_readable_answer = make_human_readable(numeric_answer, color_letters)
while num_moves < 10 and not turn_over:
print(f"Move # {num_moves} Guess : ")
user_command = input("Guess ")
if user_command == "BOARD":
print_board(guesses) # 2000
elif user_command == "QUIT": # 2500
human_readable_answer = make_human_readable(
numeric_answer, color_letters
)
print(f"QUITTER! MY COMBINATION WAS: {human_readable_answer}")
print("GOOD BYE")
quit()
elif len(user_command) != num_positions: # 410
print("BAD NUMBER OF POSITIONS")
else:
invalid_letters = get_invalid_letters(user_command)
if invalid_letters > "":
print(f"INVALID GUESS: {invalid_letters}")
else:
guess_results = compare_two_positions(
user_command, make_human_readable(numeric_answer, color_letters)
)
print(f"Results: {guess_results}")
if guess_results[1] == num_positions: # correct guess
turn_over = True
print(f"You guessed it in {num_moves} moves!")
human_score = human_score + num_moves
print_score(computer_score, human_score)
else:
print(
"You have {} blacks and {} whites".format(
guess_results[1], guess_results[2]
)
)
num_moves = num_moves + 1
guesses.append(guess_results)
if not turn_over: # RAN OUT OF MOVES
print("YOU RAN OUT OF MOVES! THAT'S ALL YOU GET!")
print(
"THE ACTUAL COMBINATION WAS: {}".format(
make_human_readable(numeric_answer, color_letters)
)
)
human_score = human_score + num_moves
print_score(computer_score, human_score)
# COMPUTER TURN
guesses = []
turn_over = False
inconsistent_information = False
while not turn_over and not inconsistent_information:
all_possibilities = [1] * possibilities
num_moves = 1
inconsistent_information = False
print("NOW I GUESS. THINK OF A COMBINATION.")
input("HIT RETURN WHEN READY: ")
while num_moves < 10 and not turn_over and not inconsistent_information:
found_guess = False
computer_guess = int(possibilities * random.random())
if (
all_possibilities[computer_guess] == 1
): # random guess is possible, use it
found_guess = True
guess = computer_guess
else:
for i in range(computer_guess, possibilities):
if all_possibilities[i] == 1:
found_guess = True
guess = i
break
if not found_guess:
for i in range(0, computer_guess):
if all_possibilities[i] == 1:
found_guess = True
guess = i
break
if not found_guess: # inconsistent info from user
print("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.")
print("TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.")
turn_over = True
inconsistent_information = True
else:
numeric_guess = [-1] * num_positions
for _ in range(0, guess):
numeric_guess = get_possibility(numeric_guess)
human_readable_guess = make_human_readable(
numeric_guess, color_letters
)
print(f"My guess is: {human_readable_guess}")
blacks_str, whites_str = input(
"ENTER BLACKS, WHITES (e.g. 1,2): "
).split(",")
blacks = int(blacks_str)
whites = int(whites_str)
if blacks == num_positions: # Correct guess
print(f"I GOT IT IN {num_moves} MOVES")
turn_over = True
computer_score = computer_score + num_moves
print_score(computer_score, human_score)
else:
num_moves += 1
for i in range(0, possibilities):
if all_possibilities[i] == 0: # already ruled out
continue
numeric_possibility = [-1] * num_positions
for _ in range(0, i):
numeric_possibility = get_possibility(
numeric_possibility
)
human_readable_possibility = make_human_readable(
numeric_possibility, color_letters
) # 4000
comparison = compare_two_positions(
human_readable_possibility, human_readable_guess
)
print(comparison)
if (blacks > comparison[1]) or (whites > comparison[2]): # type: ignore
all_possibilities[i] = 0
if not turn_over: # COMPUTER DID NOT GUESS
print("I USED UP ALL MY MOVES!")
print("I GUESS MY CPU IS JUST HAVING AN OFF DAY.")
computer_score = computer_score + num_moves
print_score(computer_score, human_score)
current_round += 1
print_score(computer_score, human_score, is_final_score=True)
sys.exit()
# 470
def get_invalid_letters(user_command) -> str:
"""Makes sure player input consists of valid colors for selected game configuration."""
valid_colors = color_letters[:num_colors]
invalid_letters = ""
for letter in user_command:
if letter not in valid_colors:
invalid_letters = invalid_letters + letter
return invalid_letters
# 2000
def print_board(guesses) -> None:
"""Print previous guesses within the round."""
print("Board")
print("Move\tGuess\tBlack White")
for idx, guess in enumerate(guesses):
print(f"{idx + 1}\t{guess[0]}\t{guess[1]} {guess[2]}")
# 3500
# Easily the place for most optimization, since they generate every possibility
# every time when checking for potential solutions
# From the original article:
# "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
# which ate tremendous amounts of memory."
def get_possibility(possibility) -> List[int]:
# print(possibility)
if possibility[0] > -1: # 3530
current_position = 0 # Python arrays are zero-indexed
while True:
if possibility[current_position] < num_colors - 1: # zero-index again
possibility[current_position] += 1
return possibility
else:
possibility[current_position] = 0
current_position += 1
else: # 3524
possibility = [0] * num_positions
return possibility
# 4500
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)."""
increment = 0
blacks = 0
whites = 0
initial_guess = guess
for pos in range(0, num_positions):
if guess[pos] != answer[pos]:
for pos2 in range(0, num_positions):
if not (
guess[pos] != answer[pos2] or guess[pos2] == answer[pos2]
): # correct color but not correct place
whites = whites + 1
answer = answer[:pos2] + chr(increment) + answer[pos2 + 1 :]
guess = guess[:pos] + chr(increment + 1) + guess[pos + 1 :]
increment = increment + 2
else: # correct color and placement
blacks = blacks + 1
# THIS IS DEVIOUSLY CLEVER
guess = guess[:pos] + chr(increment + 1) + guess[pos + 1 :]
answer = answer[:pos] + chr(increment) + answer[pos + 1 :]
increment = increment + 2
return [initial_guess, blacks, whites]
# 5000 + logic from 1160
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."""
if is_final_score:
print("GAME OVER")
print("FINAL SCORE:")
else:
print("SCORE:")
print(f" COMPUTER {computer_score}")
print(f" HUMAN {human_score}")
# 4000, 5500, 6000 subroutines are all identical
def make_human_readable(num: List[int], color_letters) -> str:
"""Make the numeric representation of a position human readable."""
retval = ""
for i in range(0, len(num)):
retval = retval + color_letters[int(num[i])]
return retval
if __name__ == "__main__":
main()
import random
import sys
from typing import List, Union
# Global variables
colors = ["BLACK", "WHITE", "RED", "GREEN", "ORANGE", "YELLOW", "PURPLE", "TAN"]
color_letters = "BWRGOYPT"
num_positions = 0
num_colors = 100
human_score = 0
computer_score = 0
def main() -> None:
global colors, color_letters, num_positions, num_colors, human_score, computer_score
colors = ["BLACK", "WHITE", "RED", "GREEN", "ORANGE", "YELLOW", "PURPLE", "TAN"]
color_letters = "BWRGOYPT"
num_colors = 100
human_score = 0
computer_score = 0
# get user inputs for game conditions
print("Mastermind")
print("Creative Computing Morristown, New Jersey")
while num_colors > 8:
num_colors = int(input("Number of colors (max 8): ")) # C9 in BASIC
num_positions = int(input("Number of positions: ")) # P9 in BASIC
num_rounds = int(input("Number of rounds: ")) # R9 in BASIC
possibilities = num_colors**num_positions
all_possibilities = [1] * possibilities
print(f"Number of possibilities {possibilities}")
print("Color\tLetter")
print("=====\t======")
for element in range(0, num_colors):
print(f"{colors[element]}\t{colors[element][0]}")
current_round = 1
while current_round <= num_rounds:
print(f"Round number {current_round}")
num_moves = 1
guesses: List[List[Union[str, int]]] = []
turn_over = False
print("Guess my combination ...")
answer = int(possibilities * random.random())
numeric_answer = [-1] * num_positions
for _ in range(0, answer):
numeric_answer = get_possibility(numeric_answer)
# human_readable_answer = make_human_readable(numeric_answer, color_letters)
while num_moves < 10 and not turn_over:
print(f"Move # {num_moves} Guess : ")
user_command = input("Guess ")
if user_command == "BOARD":
print_board(guesses) # 2000
elif user_command == "QUIT": # 2500
human_readable_answer = make_human_readable(
numeric_answer, color_letters
)
print(f"QUITTER! MY COMBINATION WAS: {human_readable_answer}")
print("GOOD BYE")
quit()
elif len(user_command) != num_positions: # 410
print("BAD NUMBER OF POSITIONS")
else:
invalid_letters = get_invalid_letters(user_command)
if invalid_letters > "":
print(f"INVALID GUESS: {invalid_letters}")
else:
guess_results = compare_two_positions(
user_command, make_human_readable(numeric_answer, color_letters)
)
print(f"Results: {guess_results}")
if guess_results[1] == num_positions: # correct guess
turn_over = True
print(f"You guessed it in {num_moves} moves!")
human_score = human_score + num_moves
print_score(computer_score, human_score)
else:
print(
"You have {} blacks and {} whites".format(
guess_results[1], guess_results[2]
)
)
num_moves = num_moves + 1
guesses.append(guess_results)
if not turn_over: # RAN OUT OF MOVES
print("YOU RAN OUT OF MOVES! THAT'S ALL YOU GET!")
print(
"THE ACTUAL COMBINATION WAS: {}".format(
make_human_readable(numeric_answer, color_letters)
)
)
human_score = human_score + num_moves
print_score(computer_score, human_score)
# COMPUTER TURN
guesses = []
turn_over = False
inconsistent_information = False
while not turn_over and not inconsistent_information:
all_possibilities = [1] * possibilities
num_moves = 1
inconsistent_information = False
print("NOW I GUESS. THINK OF A COMBINATION.")
input("HIT RETURN WHEN READY: ")
while num_moves < 10 and not turn_over and not inconsistent_information:
found_guess = False
computer_guess = int(possibilities * random.random())
if (
all_possibilities[computer_guess] == 1
): # random guess is possible, use it
found_guess = True
guess = computer_guess
else:
for i in range(computer_guess, possibilities):
if all_possibilities[i] == 1:
found_guess = True
guess = i
break
if not found_guess:
for i in range(0, computer_guess):
if all_possibilities[i] == 1:
found_guess = True
guess = i
break
if not found_guess: # inconsistent info from user
print("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.")
print("TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.")
turn_over = True
inconsistent_information = True
else:
numeric_guess = [-1] * num_positions
for _ in range(0, guess):
numeric_guess = get_possibility(numeric_guess)
human_readable_guess = make_human_readable(
numeric_guess, color_letters
)
print(f"My guess is: {human_readable_guess}")
blacks_str, whites_str = input(
"ENTER BLACKS, WHITES (e.g. 1,2): "
).split(",")
blacks = int(blacks_str)
whites = int(whites_str)
if blacks == num_positions: # Correct guess
print(f"I GOT IT IN {num_moves} MOVES")
turn_over = True
computer_score = computer_score + num_moves
print_score(computer_score, human_score)
else:
num_moves += 1
for i in range(0, possibilities):
if all_possibilities[i] == 0: # already ruled out
continue
numeric_possibility = [-1] * num_positions
for _ in range(0, i):
numeric_possibility = get_possibility(
numeric_possibility
)
human_readable_possibility = make_human_readable(
numeric_possibility, color_letters
) # 4000
comparison = compare_two_positions(
human_readable_possibility, human_readable_guess
)
print(comparison)
if ((blacks != comparison[1]) or (whites != comparison[2])): # type: ignore
all_possibilities[i] = 0
if not turn_over: # COMPUTER DID NOT GUESS
print("I USED UP ALL MY MOVES!")
print("I GUESS MY CPU IS JUST HAVING AN OFF DAY.")
computer_score = computer_score + num_moves
print_score(computer_score, human_score)
current_round += 1
print_score(computer_score, human_score, is_final_score=True)
sys.exit()
# 470
def get_invalid_letters(user_command) -> str:
"""Makes sure player input consists of valid colors for selected game configuration."""
valid_colors = color_letters[:num_colors]
invalid_letters = ""
for letter in user_command:
if letter not in valid_colors:
invalid_letters = invalid_letters + letter
return invalid_letters
# 2000
def print_board(guesses) -> None:
"""Print previous guesses within the round."""
print("Board")
print("Move\tGuess\tBlack White")
for idx, guess in enumerate(guesses):
print(f"{idx + 1}\t{guess[0]}\t{guess[1]} {guess[2]}")
# 3500
# Easily the place for most optimization, since they generate every possibility
# every time when checking for potential solutions
# From the original article:
# "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
# which ate tremendous amounts of memory."
def get_possibility(possibility) -> List[int]:
# print(possibility)
if possibility[0] > -1: # 3530
current_position = 0 # Python arrays are zero-indexed
while True:
if possibility[current_position] < num_colors - 1: # zero-index again
possibility[current_position] += 1
return possibility
else:
possibility[current_position] = 0
current_position += 1
else: # 3524
possibility = [0] * num_positions
return possibility
# 4500
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)."""
increment = 0
blacks = 0
whites = 0
initial_guess = guess
for pos in range(0, num_positions):
if guess[pos] != answer[pos]:
for pos2 in range(0, num_positions):
if not (
guess[pos] != answer[pos2] or guess[pos2] == answer[pos2]
): # correct color but not correct place
whites = whites + 1
answer = answer[:pos2] + chr(increment) + answer[pos2 + 1 :]
guess = guess[:pos] + chr(increment + 1) + guess[pos + 1 :]
increment = increment + 2
else: # correct color and placement
blacks = blacks + 1
# THIS IS DEVIOUSLY CLEVER
guess = guess[:pos] + chr(increment + 1) + guess[pos + 1 :]
answer = answer[:pos] + chr(increment) + answer[pos + 1 :]
increment = increment + 2
return [initial_guess, blacks, whites]
# 5000 + logic from 1160
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."""
if is_final_score:
print("GAME OVER")
print("FINAL SCORE:")
else:
print("SCORE:")
print(f" COMPUTER {computer_score}")
print(f" HUMAN {human_score}")
# 4000, 5500, 6000 subroutines are all identical
def make_human_readable(num: List[int], color_letters) -> str:
"""Make the numeric representation of a position human readable."""
retval = ""
for i in range(0, len(num)):
retval = retval + color_letters[int(num[i])]
return retval
if __name__ == "__main__":
main()

View File

@@ -325,7 +325,7 @@ fn main() {
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));
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;
}
}