Here is some code I have so far for a poker game and I got the def main to show some of the hands but I can't figure out to get the straight flush and royal flush. Also how would I go about doing the high card?
The card number values 0-12 are one suit, 13-25 are the next, 26-38, and 39-51.
Also if anyone can tell me how I'd implement this PokerHand class into another def main window.
class PokerHand:
"""class for representing a poker hand"""
# Poker value of hands in increasing order so they can be compared
HIGH_CARD = 0
TWO_OF_A_KIND = 1
TWO_PAIRS = 2
THREE_OF_A_KIND = 3
STRAIGHT = 4
FLUSH = 5
FULL_HOUSE = 6
FOUR_OF_A_KIND = 7
STRAIGHT_FLUSH = 8
# hand names for printing the card names
HAND_NAMES = ('High Card', 'Two of a Kind', 'Two Pairs', 'Three of a Kind',
'Straight', 'Flush', 'Full House', 'Four of a Kind',
'Straight Flush')
#------------------------------------------------------------------
def __init__(self):
"""initialize empty hand"""
self.cards = []
#------------------------------------------------------------------
def addCard(self, cardNumber):
"""add cardNumber to the hand"""
self.cards.append(cardNumber)
#------------------------------------------------------------------
def evalHand(self):
"""determine the value of the hand and return a tuple; the
first value in the tuple is an integer corresponding to the
hand value using the constants HIGH_CARD, TWO_OF_A_KIND, etc.;
the remaining values in the tuple depend on the type of hand
and are described below to break ties based on the face values
of the cards
for HIGH_CARD, it is five values: the face values sorted in
descending order
for TWO_OF_A_KIND, it is four values: the face value for the
pair, followed by the face values of the other cards in
descending order
for TWO_PAIRS, it is three values: the face value of the
higher pair, the face value of the lower pair, followed by the
face value of the other card
for THREE_OF_A_KIND, it is three values: the face value of the
three of a kind, followed by the face value of the other two
cards in descending order
for STRAIGHT, it is one value: the face value of the lowest
card in the straight
for FLUSH, it is five values: the face values sorted in
descending order
for FULL_HOUSE, it is two values: the face value of the three
of a kind, followed by the face value of the pair
for FOUR_OF_A_KIND, it is two values: the face value that
there are four of followed by the face value that there is one
of
for STRAIGHT_FLUSH, it is one value: the face value of the
lowest card in the straight"""
faces = [0,0,0,0,0,0,0,0,0,0,0,0,0]
for value in self.cards:
face = value % 13
faces[face] += 1
suits = [0,0,0,0]
for value in self.cards:
suit = value // 13
suits[suit] += 1
if faces.count(2) == 1 and faces.count(1) == 3:
return self.TWO_OF_A_KIND
elif faces.count(2) == 2 and faces.count(1) == 1:
return self.TWO_PAIRS
elif faces.count(3) == 1 and faces.count(1) == 2:
return self.THREE_OF_A_KIND
elif faces.count(3) == 1 and faces.count(2) == 1:
return self.FULL_HOUSE
elif faces.count(4) == 1 and faces.count(1) == 1:
return self.FOUR_OF_A_KIND
elif faces.count(1) == 5:
pos = faces.index(1)
if faces[pos:pos+5] == [1,1,1,1,1] or faces[pos:pos+13] == [1,0,0,0,0,0,0,0,0,1,1,1,1]:
return self.STRAIGHT
if suits.count(5) == 1 and suits.count(1) == 0:
return self.FLUSH
if suits.count(5) == 1 and faces.count(1) == 5:
pos = faces.index(1)
if faces[pos:pos + 5] == [1, 1, 1, 1, 1] or faces[pos:pos + 13] == [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]:
return self.STRAIGHT_FLUSH
return self.STRAIGHT_FLUSH
#----------------------------------------------------------------------
def main():
hand = PokerHand()
hand.addCard(9)
hand.addCard(10)
hand.addCard(8)
hand.addCard(11)
hand.addCard(7)
r = hand.evalHand()
print(r)
print(PokerHand.HAND_NAMES[r])
if __name__ == '__main__':
main()
Consider putting your hand conditionals in functions
This will help your code readability and also make it more testable.
E.g.
def is_flush(suits):
return suits.count(5) == 1
Make sure lower value hands don't short circuit higher ones
If you rearrange the order of your hand conditionals, putting higher value tests first, you will catch the more specific case first and you won't accidentally return prematurely.
You could think of hands as being of two non-exclusive types: suit based and face based. Suit conditions are "flush" or "non-flush" so it might read well to flag this up front.
Then you have a face combos, some of which "intersect" with flush_flag to increase their value.
Or... you can keep your hand test ordered the way you have, it might be more optimal since you are testing for more frequent hands earlier. If you do this make sure your conditionals are "complete". I.e. if the hand is a two-of-a-kind you must also make sure it's not a two-pair or any of the other hands that contain two-of-a-kind. You are doing this with your faces.count(2) == 1 and faces.count(1) == 3. You need to follow the same "completeness" for STRAIGHT and FLUSH.
Related
I have programmed a program of tic tac toe in python where I use the following code to determine if a row / column / diagonal consists of only ones or twos to determine if any of player one or two have won (I use ones and twos instead of x and o). The problem is that I want the winner to be chosen when there are three in a row regardless of the size of the board and with this code this only works for a board of size 3x3.
def check_row(board, player):
'''
Checks row for a win
'''
return any(all(row == player)
for row in board)
def check_column(board, player):
'''
Checks column for a win
'''
# We use transpose() to be able to check the column by using the definition
# for check_row
return check_row(board.transpose(), player)
def check_diagonal(board, player):
'''
Checks diagonal for a win
'''
# We use np.diag() and np.fliplr() to be able to check the diagonals
# of the game board
return any(all(np.diag(board_rotation) == player)
for board_rotation in [board, np.fliplr(board)])
For example, if I choose a board with size 5x5, the winner will be, with this code, when it is five in a row and not three in a row. I use np.zeros ((board_size, board_size), dtype = int) to create a game board and I have considered whether you can use something like column_1 == column_2 == column_3 to determine if there are three in a row:
if board[1][1] != 0:
if board[1][1] == board[0][0] == board[2][2]:
return board[1][1]
elif board[1][1] == board[0][2] == board[2][0]:
return board[1][1]
But also for this to work in a 5x5 game board I have to write down all possible ways to get three in a row in a 5x5 board which will give an extremely long code. Any tips to improve this?
This is my fully code now:
''' 403-0047-MPO '''
import numpy as np
import random
import matplotlib.pyplot as plt
# scenario = What scenario you would like to play, scenario one or scenario two
# game_size = The size of game board, 3x3, 5x5 or 7x7
# games = The number of game rounds you would like to play
# board = The game board
def create_board(board_size):
'''
Creating an empty board
'''
return np.zeros((board_size,board_size), dtype=int)
def available_spaces(board):
'''
Looking for available spaces on the game board
This is used in both scenario one and two
'''
lst = []
for i in range(len(board)):
for j in range(len(board)):
if board[i][j] == 0:
lst.append((i, j))
return(lst)
def make_random_move(board, player):
'''
Randomly places the player's marker on the game board
This is used in both scenario one and two
'''
spaces = available_spaces(board)
space = random.choice(spaces)
board[space] = player
return board
def available_space_middle(board, board_size):
'''
Checks that the center position is available for all game boards
This is only used in scenario two
'''
# Using if-statements to find the middle for all board_size ∈ {3, 5, 7}
# and check that they are empty, this also avoids placing the marker there repeatedly.
lst = []
for i in range(len(board)):
for j in range(len(board)):
if board_size == 3:
if board[i][j] == 0:
lst.append((1, 1))
if board_size == 5:
if board[i][j] == 0:
lst.append((2, 2))
if board_size == 7:
if board[i][j] == 0:
lst.append((3, 3))
return(lst)
def make_move_middle(board, player, board_size):
'''
Places the first marker for player one in the center of the game board.
This is only used in scenario two
'''
# Using player = 1 we define that the first move in scenario two should be
# made by player one using the marker 1
player = 1
selection = available_space_middle(board, board_size)
current_loc = random.choice(selection)
board[current_loc] = player
return board
def check_row(board, player):
'''
Checks row for a win
'''
return any(all(row == player)
for row in board)
def check_column(board, player):
'''
Checks column for a win
'''
# We use transpose() to be able to check the column by using the definition
# for check_row
return check_row(board.transpose(), player)
def check_diagonal(board, player):
'''
Checks diagonal for a win
'''
# We use np.diag() and np.fliplr() to be able to check the diagonals
# of the game board
return any(all(np.diag(board_rotation) == player)
for board_rotation in [board, np.fliplr(board)])
def evaluate(board):
'''
Evaluates the game board for a winner
'''
# Here we bring out the winner and define 1 for when player one wins,
# 2 for when player two wins and -1 for ties.
winner = 0
for player in [1, 2]:
if (check_row(board, player) or
check_column(board,player) or
check_diagonal(board,player)):
winner = player
if np.all(board != 0) and winner == 0:
winner = -1
return winner
def play_game(scenario, board_size):
'''
Defines the game for scenario one and scenario two
'''
# By using if-statements and a while-loop we define how the game should
# go in scenario one and scenario two respectively.
# By using break, we also ensure that the loop (game) ends if a winner has been found.
board, winner, counter = create_board(board_size), 0, 1
if scenario == 1:
while winner == 0:
for player in [1, 2]:
board = make_random_move(board, player)
# Remove the hashtags below to check the game board step by step
print("Board after " + str(counter) + " move")
print(board)
counter += 1
winner = evaluate(board)
if winner != 0:
break
return winner
if scenario == 2:
while winner == 0:
for player in [2, 1]:
board = make_move_middle(board, player, board_size)
board = make_random_move(board, player)
# Remove the hashtags below to check the game board step by step
print("Board after " + str(counter) + " move")
print(board)
counter += 1
winner = evaluate(board)
if winner != 0:
break
return winner
def save_stats(games, scenario, board_size):
'''
Saves data from all game rounds
'''
# By using the previously defined 1,2 and -1 we add one point for each win
# for player1wins, player2wins and ties.
player1wins=0
player2wins=0
ties=0
for game in range(games):
result=play_game(scenario, board_size)
if result==-1: ties+=1
elif result==1: player1wins+=1
else: player2wins+=1
return [player1wins, player2wins, ties] # for returning
def print_stats(games, scenario, board_size):
'''
Presents data from all game rounds
'''
player1wins, player2wins, ties = save_stats(games, scenario, board_size)
print('Player 1 wins:',player1wins)
print('Player 2 wins:',player2wins)
print('Tie:',ties)
# Data
height = [player1wins, player2wins, ties]
bars = ('Player 1', 'Player 2', 'Tie')
y_pos = np.arange(len(bars))
# Create bars and choose color
plt.bar(y_pos, height, color = (0.5,0.1,0.5,0.6))
# Limits for the Y axis
plt.ylim(0,games)
# Create names
plt.xticks(y_pos, bars)
# Saves a pdf with data
plt.savefig('utfall.pdf') # Saves the data as 'utfall.pdf'
plt.close()
def main():
'''
This is the main body of the program
'''
# Using try: and except: to raise ValueError and
# while-loops to handle incorrect input. The question will be asked again
# if the input value is wrong and continue via break if it is correct.
try:
while True:
games = int(input("How many games do you want to simulate? "))
if games <1:
print('The number of game rounds should be grater than zero, try again')
else:
break
while True:
board_size = int(input("How big playing surface (3/5/7)? "))
if not (board_size == 3 or board_size == 5 or board_size == 7):
print('Board size does not exist, try again')
else:
break
while True:
scenario = int(input('What scenario (1/2)? '))
if not (scenario == 1 or scenario == 2):
print('Scenario does not exist, try again')
else:
break
print_stats(games, scenario, board_size)
except ValueError:
print('All questions must be answered correctly for the game to start, try again')
main()
I will post a idea using numpy.split
You split your board in chunks of 3, but starting in different positions: 0, 1 and 2.
Explanation: Consider [a,b,c,d,f,g]
Starting from 0 -> [a,b,c], [d,f,g]
Starting from 1 -> [b,c,d]
Starting from 2 -> [c,d,f]
so, all possibilities are there
You check if the elements are equal 1 or 2 and sum over the boolean mask. If the sum is 3, then there is a chunk with 3 equal elements.
Example code to search from winners on the rows:
import numpy as np
#random array just for test
board = np.array([[1,-1,-1,-1,1,0],
[1,1,0,1,1,0],
[1,1,0,1,1,0],
[1,1,0,1,1,0],
[1,1,0,1,1,0]])
winner = None
#iterate on i to start from position 0, 1 and 2
for i in range(3):
#We want the lengh of the array a multiple of 3, so we must correct were it ends:
rest = -(board[:,i:].shape[1]%3)
if rest == 0:
#0 would return a empty list, so if it is the case, we will change to None
rest = None
#Now, we calculate how manny divisions we need to get chunks of 3 (on the rows)
n = (board[:,i:].shape[1])//3
#Now divide the board in chunks
chunks = np.split(board[:,i:rest],n,axis = 1)
for matrix in chunks:
#sum over all rows of 3 elements
sumation = np.sum(matrix == 1,axis = 1)
if 3 in sumation:
winner = 1
sumation = np.sum(matrix == 2,axis = 1)
if 3 in sumation:
winner = 2
Well, maybe a little confusing, but i believe it will work, or at least give you some idea
I have 3 die, each with 13 sides. You have to "roll" them until you get all three to be the same value (shown below).
ct = 1
import random
min = 1
max = 13
roll_again = "yes"
while roll_again == "yes" or roll_again == "y" or roll_again == "":
print "This is attempt number: ", + ct
print "Now, rolling the dice..."
print "The values are...."
die1 = random.randint(min, max)
die2 = random.randint(min, max)
die3 = random.randint(min, max)
print die1
print die2
print die3
ct += 1
if die1 == die2:
if die2 == die3:
print "Congrats! You win! hooray! You got it on attempt number ", + ct
ct = 1
roll_again = raw_input("Roll the dice again? ")
This all works fine,
but I wanted the 1 face to be able to come up 5 times more than the rest (like a weighted die), and the face 13 to come up .5 times than the others (excluding the 1 value of course). Is this possible in Python 2.7?
I once posted an answer to a similar question (albeit in Java) in which someone wanted to choose letters of the alphabet with varying probabilities. You could take either of the approaches I suggested there.
The most general approach is to calculate the probability of choosing each number. You might have a list of (number, probability) tuples, for example
choices = [(1, 0.2), (2, 0.05), (3, 0.3113), ...]
and then you can make your random choice by choosing a random number between 0 and 1, iterating over the list, adding up probabilities as you go, and when you reach a sum which is larger than the random number you chose, stop and return the corresponding choice.
def random_choice(choices):
r = random.random()
cdf = 0
for number, cumulative_probability in choices:
cdf += cumulative_probability
if r < cdf:
return number
return choices[-1][0]
(Note that itertools.accumulate() doesn't exist in Python 2, otherwise you could use that here.)
If you happen to be using Numpy, it has this functionality built in as numpy.random.choice(numbers, p=probabilities).
If all your probabilities are multiples of a certain value, though, you might be better off using random.choice(). To do this, you'd create a list of the possible choices without probabilities, repeating each one enough to get the relative proportions you want. For example,
choices = [1, 1, 1, 2, 3, 3, 4]
would cause 1 to be three times as likely as 2 or 4, and 3 to be twice as likely as 2 or 4. Then just call random.choice(choices) each time you want to roll the die.
In your case, the second approach seems well suited, but you could use either. I'll leave it to you to figure out the proper definition of choices for your case. ;-)
I think the tool you're looking for is numpy.random.choice()
This tool allows you to generate a number following a non equiprobable probability law by giving list of the probability of all events as parameter
probability_A = 0.1
probability_B = 0.5
probability_C = 0.4
print(numpy.random.choice(numpy.arange(1, 4), p = [probability_A, probability_B, probability_C]))
In your situation, you first need to calculate the probabilities.
If you know that the number "1" has a weight of 5 and the number "13" a weight of five, you can generate a vector (a list in Python language) this way:
weights = [5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.5]
probabilities = [weight/sum(weights) for weight in weights]
And finally
numpy.random.choice(numpy.arange(1, 14), p = probabilities)
I was making a poker simulation when I got stuck on how to make python check an array for pairs, straights, three of a kinds etc. i have made my code so each array of cards generates a second array with the stores value for each card.
For example
a=[1,3,4,2,5,8,1] b=[2,2,4,7,10] c=[5,6,5,5,5]
how would I check if a had at least 5 consecutive numbers (a straight) if b had at least 2 numbers equal to each other (a pair) and if c had 4.
This should be enough to get you started.
def check_hand(_hand_):
last_c = ''
straight = 0
# This is illustrative, you can use this to return
# the greatest hand one could have
for card_value, number_of_occurences in _hand_.iteritems():
if number_of_occurences == 2:
print("We have a 2 of a kind")
elif number_of_occurences == 3:
print("We have a 3 of a kind")
elif number_of_occurences == 4:
print("We have a 4 of a kind")
if last_c == '':
last_c = card_value
else:
if card_value - last_c == 1:
straight += 1
last_c = card_value
if straight >= 4:
print("we have a straight")
a = [1, 3, 4, 2, 5, 8, 1]
b = [2, 2, 4, 7, 10]
c = [5, 6, 5, 5, 5]
# nifty way of assigning a dictionary with how many
# occurrences of a number in a list, and sorting it
check = {
'a': dict((i, a.count(i)) for i in sorted(a)),
'b': dict((i, b.count(i)) for i in sorted(b)),
'c': dict((i, c.count(i)) for i in sorted(c)),
}
for which, hand in check.iteritems():
print "Hand: " + which
check_hand(hand)
Sort the hand. This makes it easy to check for a straight, as you need the five numbers in sequence.
For N of a kind, walk through the list and see how many of each item you have. For instance:
for pos in range(len(hand)):
card_count = hand.count(hand[pos])
if card_count >= 2:
print "Hand has", card_count, hand[pos], "'s"
I haven't given away all the details here -- this will print twice for each pair, 3 times for 3-of-a-kind, etc. I assume that you're asking most for the basic list methods you'll need.
I'm trying to understand this solution of the Monty Hall problem, I understand most of the code, but am stuck on two pieces.
Below is the code, but specifically I'm stuck on these two parts
result[bad] = np.random.randint(0,3, bad.sum())
and the entire switch_guess function.
If anyone could explain in plain English for me that would be awesome.
#Simulates picking a prize door
def simulate_prizedoor(nsim):
return np.random.randint(0,3,(nsim))
#Simulates the doors guessed
def simulate_guesses(nsim):
return np.zeros(nsim, dtype=np.int)
#Simulates the "game host" showing whats behind a door
def goat_door(prize_doors, guesses):
result = np.random.randint(0,3, prize_doors.size)
while True:
bad = (result == prize_doors) | (result == guesses)
if not bad.any():
return result
result[bad] = np.random.randint(0,3, bad.sum())
#Used to change your guess
def switch_guess(guesses, goat_doors):
result = np.zeros(guesses.size)
switch = {(0, 1): 2, (0, 2): 1, (1, 0): 2, (1, 2): 1, (2, 0): 1, (2, 1): 0}
for i in [0,1,2]:
#print "i = ", i
for j in [0,1,2]:
#print "j = ", j
mask = (guesses == i) & (goat_doors == j)
#print "mask = ", mask
if not mask.any():
continue
result = np.where(mask, np.ones_like(result) * switch[(i, j)], result)
return result
#Calculates the win percentage
def win_percentage(guesses, prizedoors):
return 100 * (guesses == prizedoors).mean()
#The code to pull everything together
nsim = 10000
#keep guesses
print "Win percentage when keeping original door"
print win_percentage(simulate_prizedoor(nsim), simulate_guesses(nsim))
#switch
pd = simulate_prizedoor(nsim)
guess = simulate_guesses(nsim)
goats = goat_door(pd, guess)
guess = switch_guess(guess, goats)
print "Win percentage when switching doors"
print win_percentage(pd, guess)
… specifically I'm stuck on these two parts
result[bad] = np.random.randint(0,3, bad.sum())
Let's break this down into pieces. It may help to reduce that 10000 to something small, like 5, so you can print out the values (either with print calls, or in the debugger) and see what's going on.
When we start this function, prize_doors is going to have 5 random values from 0 to 2, like 2 2 0 1 2, and guesses will have 5 values, all 0, like 0 0 0 0 0. result will therefore start off with 5 random values from 0 to 2, like 0 2 2 0 1.
Each first time through the loop, bad will be a list of 5 bool values, which are each True if the corresponding value in result matches the corresponding value in either prize_doors or guesses. So, in this example, True True False True False, because guess #1 matches prize_doors, and guesses #0 and #3 match goats.
Unfortunately, we're just going to go around that loop forever, because there's nothing inside the loop that modifies result, and therefore bad is going to be the same forever, and doing the same check forever is always going to return the same values.
But if you indent that result[bad] = … line so it's inside the loop, that changes everything. So, let's assume that's what you were supposed to do, and you just copied it wrong.
When treated as numbers, True and False have values 1 and 0, respectively. So, bad.sum() is a count of how many matches there were in bad—in this case, 3.
So, np.random.randint(0, 3, bad.sum()) picks 3 random values from 0 to 2, let's say 1 0 1.
Now, result[bad] selects all of the elements of result for which the corresponding value in bad is True, so in this example it's result[0], result[1], and result[3].
So we end up assigning that 1 0 1 to those three selected locations, so result is now 1 0 2 1 1.
So, next time through the loop, bad is now True False False False False. We've still got at least one True value, so we run that result[bad] = line again. This time, bad.sum() is 1, so we pick 1 random value, let's say 0, and we then assign that 1 value to result[0], so result is now 0 0 2 1 1.
The next time through, bad is now False False False False False, so bad.any() is False, so we're done.
In other words, each time through, we take all the values that don't match either the prize door or the goat door, and pick a new door for them, until finally there are no such values.
It also confused me, until 5 mins ago when I finally figured it out.
Since the first question has been solved, I will only talk about the second one.
The intuition goes like this : given a sequence of (guesses, goatdoors),in the (i,j) loop, there are always some simulation (e.g., simulation[0] and simulation[5]) that 'hit' by the (i,j), that is the say, the 0th and 5th simulation have guess i and goatdoor j.
Variable mask record 0 and 5 in this example. Then result in 0th and 5th can be decided, because in these simulation, the only possible door to switch to is determined by i and j. So np.where refreshes result in these simulation, leave other simulations unchanged.
Intuition is above. You need to know how np.where work if you want to know what I'm talking about. Good luck.
I have a list of favourite movies and I'd like to sort them according to my taste from the best movies (have most points) to worst movie (has only 1 point).
Lets say the list contains already 300 sorted movies and you want to determine points for the new movie. You could compare the new movie with every movie in the sorted list or you can utilize the knowledge that the list is sorted.
I tried to implement it as a binary search so every insert (of new movie) has logarithmic complexity.
The binary search implementation was easy for me:
def binSearch(lst, number):
left = 0
right = len(lst) - 1
while left <= right:
middle = left + ((right - left) / 2)
if number == lst[middle]:
return True
else:
if number < lst[middle]:
right = middle - 1
else:
left = middle + 1
return False
But determining points is quite difficult for me. I'm already debugging it for a few hours and still some errors occur. I changed the implementation many times but nothing helps.
Here is my last solution (maybe the algorithm is in the worse state then it was at the beginning)
def determinePoints(lst, new):
# lst is a list of tuples with movies
# new is a tuple with new movie (has no point associated yet)
if not lst: # no movie has points so far
return 1 # now exists only one movie with associated points
newTitle = new[0]
newGenre = new[1]
atMost = len(lst)
atLeast = 0
while atLeast < atMost - 1: # algorithm doesn't work
# if only two movies have associated points
center = twoPointsCenter(atLeast, atMost)
(title, genre) = lst[center]
os.system("clear")
competitionStrings = [ newTitle, newGenre, "\n" * 10, title, genre ]
print "\n".join([ x.center(150) for x in competitionStrings ])
c = getch()
if c == "j": # new movie is worse than this
atMost = center - 1
if atMost <= 1:
return 1
else: # new movie is better than this
atLeast = center + 1
if atLeast >= len(lst):
return max(atLeast, 1)
return max(twoPointsCenter(atLeast, atMost), 1)
def twoPointsCenter(left, right):
return left + ((right - left) / 2)
Could you correct my solution (or implement it better) to converge and end with the right result?
It should work with lst of lengths from 0, 1, 2, ... etc. It shouldn't return value less than 1. In the list of movies there shouldn't be two movies with the same number of points.
When the function determinePoints returns points, I will update the database for this movie and increment points by 1 for each movie with >= points than this new movie.
Thank you
I think you need to better look at the boundary indexes. len(lst) is one larger than the maximal index, for example: lists are 0-based. I took the liberty to use 0 as lowest possible score; this will directly give you the position to lst.insert at. Also, I couldn't resist and made this a little more PEP 8-like.
You don't need all the corner cases; they just work fine, I think.
def determine_points(lst, new):
# lst is a list of tuples with movies, ranked by how good the movie is
# new is a tuple with new movie
# the new movies position is to be determined
new_title, new_genre = new
at_most = len(lst)
at_least = 0
while at_least < at_most:
center = (at_least + at_most) // 2
title, genre = lst[center]
os.system("clear")
competition_strings = [new_title, new_genre, "\n" * 10, title, genre]
print("\n".join(x.center(150) for x in competition_strings))
c = getch()
if c == "j": # new movie is worse than this
at_most = center
else: # new movie is better than this
at_least = center + 1
return at_least
Edit: I tested with the following code.
lst = []
news = [(str(i), str(i)) for i in range(10)]
import random
random.shuffle(news)
for new in news:
print(lst)
lst.insert(determine_points(lst, new), new)
print(lst)