I am trying to set up a recursive game solver (for the cracker-barrel peg game). The recursive function appears to not be operating correctly, and some outputs are created with no trace of how they were created (despite logging all steps). Is it possible that the python recursion steps are overwriting eachother?
I have already tried adding in print statements at all steps of the way. The game rules and algorithms work correctly, but the recursive play algorithm is not operating as expected
def recursive_play(board, moves_list, move_history, id, first_trial, recurse_counter):
# Check how many moves are left
tacks_left = len(char_locations(board, character=tack, grid=True))
log_and_print(f"tacks_left: {tacks_left}")
log_and_print(f"moves_left: {len(moves_list)}")
log_and_print(f"moves_list: {moves_list}")
if (len(moves_list) == 0):
if (tacks_left == 1):
# TODO: Remove final move separator
log_and_print(f"ONE TACK LEFT :)!!!!")
log_and_print(f"move_history to retrun for win: {move_history}")
return move_history
pass
elif (len(moves_list) > 0):
# Scan through all moves and make them recursively
for move in moves_list:
if first_trial:
id += 1
else:
# id += 1
id = id
next_board = make_move(board, move)
next_moves = possible_moves(next_board)
if first_trial:
next_history = "START: " + move
else:
next_history = move_history + round_separator + move
# log_and_print(f"og_board:")
prettify_board(board)
log_and_print(f"move: {move}")
log_and_print(f"next_board:")
prettify_board(next_board)
# log_and_print(f"next_moves: {next_moves}")
log_and_print(f"next_history: {next_history}")
log_and_print(f"id: {id}")
log_and_print(f"recurse_counter: {recurse_counter}")
# NOTE: Would this be cleaner with queues?
recursive_play(next_board, moves_list=next_moves, move_history=next_history, id=id, first_trial=False, recurse_counter=recurse_counter+1)
log_and_print(f"finished scanning all moves for board: {board}")
I expect all steps to be logged, and "START" should only occur on the first trial. However, a mysterious "START" appears in a later step with no trace of how that board was created.
Good Output:
INFO:root:next_history: START: 4 to 2 to 1 , 6 to 5 to 4 , 1 to 3 to 6 , 7 to 4 to 2
INFO:root:id: 1
INFO:root:recurse_counter: 3
INFO:root:tacks_left: 5
INFO:root:moves_left: 2
INFO:root:moves_list: ['9 to 8 to 7', '10 to 6 to 3']
INFO:root:o---
INFO:root:xo--
INFO:root:oox-
INFO:root:xoox
INFO:root:move: 9 to 8 to 7
INFO:root:next_board:
INFO:root:o---
INFO:root:xo--
INFO:root:oox-
INFO:root:xoox
INFO:root:next_history: START: 4 to 2 to 1 , 6 to 5 to 4 , 1 to 3 to 6 , 7 to 4 to 2 , 9 to 8 to 7
INFO:root:id: 1
INFO:root:recurse_counter: 4
INFO:root:tacks_left: 4
INFO:root:moves_left: 1
INFO:root:moves_list: ['10 to 6 to 3']
INFO:root:o---
INFO:root:xx--
INFO:root:ooo-
INFO:root:xooo
INFO:root:move: 10 to 6 to 3
INFO:root:next_board:
INFO:root:o---
INFO:root:xx--
INFO:root:ooo-
INFO:root:xooo
INFO:root:next_history: START: 4 to 2 to 1 , 6 to 5 to 4 , 1 to 3 to 6 , 7 to 4 to 2 , 9 to 8 to 7 , 10 to 6 to 3
Bad Output:
INFO:root:move: 6 to 3 to 1
INFO:root:next_board:
INFO:root:x---
INFO:root:xo--
INFO:root:ooo-
INFO:root:oooo
INFO:root:next_history: START: 6 to 3 to 1
INFO:root:id: 2
INFO:root:recurse_counter: 0
INFO:root:tacks_left: 2
INFO:root:moves_left: 1
INFO:root:moves_list: ['1 to 2 to 4']
INFO:root:o---
INFO:root:oo--
INFO:root:xoo-
INFO:root:oooo
INFO:root:move: 1 to 2 to 4
INFO:root:next_board:
INFO:root:o---
INFO:root:oo--
INFO:root:xoo-
INFO:root:oooo
INFO:root:next_history: START: 6 to 3 to 1 , 1 to 2 to 4
INFO:root:id: 2
INFO:root:recurse_counter: 1
INFO:root:tacks_left: 1
INFO:root:moves_left: 0
INFO:root:moves_list: []
INFO:root:ONE TACK LEFT :)!!!!
INFO:root:move_history to retrun for win: START: 6 to 3 to 1 , 1 to 2 to 4
INFO:root:finished scanning all moves for board: ['o---', 'oo--', 'xoo-', 'oooo']
Any tips anyone can provide would be greatly appreciated.
Related
I am working on a small task in which I have to find the distance between two nodes. Each node has X and Y coordinates which can be seen below.
node_number X_coordinate Y_coordinate
0 0 1 0
1 1 1 1
2 2 1 2
3 3 1 3
4 4 0 3
5 5 0 4
6 6 1 4
7 7 2 4
8 8 3 4
9 9 4 4
10 10 4 3
11 11 3 3
12 12 2 3
13 13 2 2
14 14 2 1
15 15 2 0
For the purpose I mentioned above, I wrote below code,
X1_coordinate = df['X_coordinate'].tolist()
Y1_coordinate = df['Y_coordinate'].tolist()
node_number1 = df['node_number'].tolist()
nodal_dist = []
i = 0
for i in range(len(node_number1)):
dist = math.sqrt((X1_coordinate[i+1] - X1_coordinate[i])**2 + (Y1_coordinate[i+1] - Y1_coordinate[i])**2)
nodal_dist.append(dist)
I got the error
list index out of range
Kindly let me know what I am doing wrong and what should I change to get the answer.
Indexing starts at zero, so the last element in the list has an index that is one less than the number of elements in that list. But the len() function gives you the number of elements in the list (in other words, it starts counting at 1), so you want the range of your loop to be len(node_number1) - 1 to avoid an -off-by-one error.
The problems should been in this line
dist = math.sqrt((X1_coordinate[i+1] - X1_coordinate[i])**2 + (Y1_coordinate[i+1] - Y1_coordinate[i])**2)
the X1_coordinate[i+1] and the ] Y1_coordinate[i+1]] go out of range on the last number call.
I'm trying to write a script to randomise a round-robin schedule for a tournament.
The constraints are:
8 Teams
Teams face each other twice, once at home and once away
14 weeks, one game for each team per week
My code works fine in theory, but when it's generated it sometimes freezes on certain weeks when there are only two teams left for that week, and both possible games have already been played. I use a numpy array to check which matchups have been played.
At the moment my code looks like this:
import random
import numpy
regular_season_games = 14
regular_season_week = 0
checker = numpy.full((8,8), 0)
for x in range (0,8):
checker[x][x] = 1
teams_left = list(range(8))
print ("Week " + str(regular_season_week+1))
while (regular_season_week < regular_season_games):
game_set = False
get_away_team = False
while get_away_team == False:
Team_A = random.choice(teams_left)
if 0 in checker[:,Team_A]:
for x in range (0,8):
if checker[x][Team_A] == 0 and x in teams_left:
teams_left.remove(Team_A)
get_away_team = True
break
while game_set == False:
Team_B = random.choice(teams_left)
if checker[Team_B][Team_A] == 0:
teams_left.remove(Team_B)
print(str(Team_A) + " vs " + str(Team_B))
checker[Team_B][Team_A] = 1
game_set = True
if not teams_left:
print ("Week " + str(regular_season_week+2))
teams_left = list(range(8))
regular_season_week = regular_season_week + 1
I've used an adaptation of the scheduling algorithm from here to achieve this. Basically, we generate a list of the teams - list(range(8)) - and choose as our initial matchup 0 vs 4, 1 vs 5, 2 vs 6, 3 vs 7. We then rotate the list, excluding the first element, and choose as our next matchup 0 vs 3, 7 vs 4, 1 vs 5, 2 vs 6. We continue on in the following way until we have every pairing.
I've added a handler for home & away matches - if a pairing has already been played, we play the opposite home/away pairing. Below is the code, including a function to check if a list of games is valid, and a sample output.
Code:
import random
# Generator function for list of matchups from a team_list
def games_from_list(team_list):
for i in range(4):
yield team_list[i], team_list[i+4]
# Function to apply rotation to list of teams as described in article
def rotate_list(team_list):
team_list = [team_list[4]] + team_list[0:3] + team_list[5:8] + [team_list[3]]
team_list[0], team_list[1] = team_list[1], team_list[0]
return team_list
# Function to check if a list of games is valid
def checkValid(game_list):
if len(set(game_list)) != len(game_list):
return False
for week in range(14):
teams = set()
this_week_games = game_list[week*4:week*4 + 4]
for game in this_week_games:
teams.add(game[0])
teams.add(game[1])
if len(teams) < 8:
return False
else:
return True
# Generate list of teams & empty list of games played
teams = list(range(8))
games_played = []
# Optionally shuffle teams before generating schedule
random.shuffle(teams)
# For each week -
for week in range(14):
print(f"Week {week + 1}")
# Get all the pairs of games from the list of teams.
for pair in games_from_list(teams):
# If the matchup has already been played:
if pair in games_played:
# Play the opposite match
pair = pair[::-1]
# Print the matchup and append to list of games.
print(f"{pair[0]} vs {pair[1]}")
games_played.append(pair)
# Rotate the list of teams
teams = rotate_list(teams)
# Checks that the list of games is valid
print(checkValid(games_played))
Sample Output:
Week 1
0 vs 7
4 vs 3
6 vs 1
5 vs 2
Week 2
0 vs 3
7 vs 1
4 vs 2
6 vs 5
Week 3
0 vs 1
3 vs 2
7 vs 5
4 vs 6
Week 4
0 vs 2
1 vs 5
3 vs 6
7 vs 4
Week 5
0 vs 5
2 vs 6
1 vs 4
3 vs 7
Week 6
0 vs 6
5 vs 4
2 vs 7
1 vs 3
Week 7
0 vs 4
6 vs 7
5 vs 3
2 vs 1
Week 8
7 vs 0
3 vs 4
1 vs 6
2 vs 5
Week 9
3 vs 0
1 vs 7
2 vs 4
5 vs 6
Week 10
1 vs 0
2 vs 3
5 vs 7
6 vs 4
Week 11
2 vs 0
5 vs 1
6 vs 3
4 vs 7
Week 12
5 vs 0
6 vs 2
4 vs 1
7 vs 3
Week 13
6 vs 0
4 vs 5
7 vs 2
3 vs 1
Week 14
4 vs 0
7 vs 6
3 vs 5
1 vs 2
True
anyone can help me. I am working on the traffic light and I want to make a delay for green and red lights and print so I tried this code:
import time
t=10
while True:
time.sleep(1)
print(t)
t = t - 1
if(t==0):
break
but it repeated twice:
10
9
8
7
6
5
4
3
2
1
10
9
8
7
6
5
4
3
2
1
i expected :
10
9
8
7
6
5
4
3
2
1
0
This worked for me:
import time
t = 10
while (t >= 0):
time.sleep(1)
print(t)
t -= 1
change if(t ==0): to if(t<0):
if t==0, it will print upto 1(ie from 10-1),it stops when t=0.
if want to include 0, then change the to t<0.so it prints upto 0(from 10-0), it stops when t value is less than 0.
check out this code:
code:
import time
t=10
while True:
time.sleep(1)
print(t)
t = t - 1
if(t < 0):
break
output:
10
9
8
7
6
5
4
3
2
1
0
I'm trying to parse a logfile of our manufacturing process. Most of the time the process is run automatically but occasionally, the engineer needs to switch into manual mode to make some changes and then switches back to automatic control by the reactor software. When set to manual mode the logfile records the step as being "MAN.OP." instead of a number. Below is a representative example.
steps = [1,2,2,'MAN.OP.','MAN.OP.',2,2,3,3,'MAN.OP.','MAN.OP.',4,4]
ser_orig = pd.Series(steps)
which results in
0 1
1 2
2 2
3 MAN.OP.
4 MAN.OP.
5 2
6 2
7 3
8 3
9 MAN.OP.
10 MAN.OP.
11 4
12 4
dtype: object
I need to detect the 'MAN.OP.' and make them distinct from each other. In this example, the two regions with values == 2 should be one region after detecting the manual mode section like this:
0 1
1 2
2 2
3 Manual_Mode_0
4 Manual_Mode_0
5 2
6 2
7 3
8 3
9 Manual_Mode_1
10 Manual_Mode_1
11 4
12 4
dtype: object
I have code that iterates over this series and produces the correct result when the series is passed to my object. The setter is:
#step_series.setter
def step_series(self, ss):
"""
On assignment, give the manual mode steps a unique name. Leave
the steps done on recipe the same.
"""
manual_mode = "MAN.OP."
new_manual_mode_text = "Manual_Mode_{}"
counter = 0
continuous = False
for i in ss.index:
if continuous and ss.at[i] != manual_mode:
continuous = False
counter += 1
elif not continuous and ss.at[i] == manual_mode:
continuous = True
ss.at[i] = new_manual_mode_text.format(str(counter))
elif continuous and ss.at[i] == manual_mode:
ss.at[i] = new_manual_mode_text.format(str(counter))
self._step_series = ss
but this iterates over the entire dataframe and is the slowest part of my code other than reading the logfile over the network.
How can I detect these non-unique sections and rename them uniquely without iterating over the entire series? The series is a column selection from a larger dataframe so adding extra columns is fine if needed.
For the completed answer I ended up with:
#step_series.setter
def step_series(self, ss):
pd.options.mode.chained_assignment = None
manual_mode = "MAN.OP."
new_manual_mode_text = "Manual_Mode_{}"
newManOp = (ss=='MAN.OP.') & (ss != ss.shift())
ss[ss == 'MAN.OP.'] = 'Manual_Mode_' + (newManOp.cumsum()-1).astype(str)
self._step_series = ss
Here's one way:
steps = [1,2,2,'MAN.OP.','MAN.OP.',2,2,3,3,'MAN.OP.','MAN.OP.',4,4]
steps = pd.Series(steps)
newManOp = (steps=='MAN.OP.') & (steps != steps.shift())
steps[steps=='MAN.OP.'] += seq.cumsum().astype(str)
>>> steps
0 1
1 2
2 2
3 MAN.OP.1
4 MAN.OP.1
5 2
6 2
7 3
8 3
9 MAN.OP.2
10 MAN.OP.2
11 4
12 4
dtype: object
To get the exact format you listed (starting from zero instead of one, and changing from "MAN.OP." to "Manual_mode_"), just tweak the last line:
steps[steps=='MAN.OP.'] = 'Manual_Mode_' + (seq.cumsum()-1).astype(str)
>>> steps
0 1
1 2
2 2
3 Manual_Mode_0
4 Manual_Mode_0
5 2
6 2
7 3
8 3
9 Manual_Mode_1
10 Manual_Mode_1
11 4
12 4
dtype: object
There a pandas enhancement request for contiguous groupby, which would make this type of task simpler.
There is s function in matplotlib that takes a boolean array and returns a list of (start, end) pairs. Each pair represents a contiguous region where the input is True.
import matplotlib.mlab as mlab
regions = mlab.contiguous_regions(ser_orig == manual_mode)
for i, (start, end) in enumerate(regions):
ser_orig[start:end] = new_manual_mode_text.format(i)
ser_orig
0 1
1 2
2 2
3 Manual_Mode_0
4 Manual_Mode_0
5 2
6 2
7 3
8 3
9 Manual_Mode_1
10 Manual_Mode_1
11 4
12 4
dtype: object
I have a sheet of numbers, separated by spaces into columns. Each column represents a different category, and within each column, each number represents a different value. For example, column number four represents age, and within the column, the number 5 represents an age of 44-55. Obviously, each row is a different person's record. I'd like to use a Python script to search through the the sheet, and find all columns where the sixth column is number "1." After that, I want to know how many times each number in column one appears where the number in column six is equal to "1." The script should output to the user that "While column six equals '1', the value '1' appears 12 times in column one. The value '2' appears 18 times..." etc. I hope I'm being clear here. I just want it to list the numbers, basically. Anyway, I'm new to Python. I've attached my code below. I think I should be using dictionaries, but I'm just not totally sure how. So far, I haven't really come close to figuring this out. I would really appreciate if someone could walk me through the logic that would be behind such code. Thank you so much!
ldata = open("list.data", "r")
income_dist = {}
for line in ldata:
linelist = line.strip().split(" ")
key_income_dist = linelist[6]
if key_income_dist in income_dist:
income_dist[key_income_dist] = 1 + income_dist[key_income_dist]
else:
income_dist[key_income_dist] = 1
ldata.close()
print value_no_occupations
First, indentation is majorly important in Python and the above is bad: the 5 lines following linelist = line.strip().split(" ") need to be indented to be in the loop like they should be.
Next they should be indented further and this line added before them:
if len(linelist)>6 and linelist[6]=="1":
This line skips over short lines (there are some), and tests for what you said you wanted: "where column six equals "1."" This is column [6] where the first number on the line is referenced as [0] (these are "offsets", not "cardinal", or counting, numbers).
You'll probably want to change key_income_dist = linelist[6] to key_income_dist = linelist[0] or [1] to get what you want. Play around if necessary.
Finally, you should say print income_dist at the end to get a look at your results. If you want fancier output, study up on formatting.
This is actually easier than it seems! The key is collections.Counter
from collections import Counter
ldata = open("list.data")
rows = [tuple(row.split()) for row in ldata if row.split()[5]==1]
# warning this will break if some rows are shorter than 6 columns
first_col = Counter(item[0] for item in rows)
If you want the distribution of every column (not just the first) do:
distribution = {column: Counter(item[column] for item in rows) for column in range(len(rows[0]))}
# warning this will break if all rows are not the same size!
Considering that the data file has ~9000 rows of data, if you don't want to keep the original data, you can combine step 1 & 2 to make the program use less memory and a little faster.
ldata = open("list.data", "r")
# read in all the rows, note that the list values are strings instead of integers
# keep only the rows with 6th column = '1'
only1 = []
for line in ldata:
if line.strip() == '': # ignor blank lines
continue
row = tuple(line.strip().split(" "))
if row[5] == '1':
only1.append(row)
ldata.close()
# tally the statistics
income_dist = {}
for row in only1:
if row[0] in income_dist:
income_dist[row[0]] += 1
else:
income_dist[row[0]] = 1
# print result
print "While column six equals '1',"
for num in sorted(income_dist):
print "the value %s appears %d times in column one." % (num, income_dist[num])
Sample Test Data in list.data:
9 2 1 5 4 5 5 3 3 0 1 1 7 NA
9 1 1 5 5 5 5 3 5 2 1 1 7 1
9 2 1 3 5 1 5 2 3 1 2 3 7 1
1 2 5 1 2 6 5 1 4 2 3 1 7 1
1 2 5 1 2 6 3 1 4 2 3 1 7 1
8 1 1 6 4 8 5 3 2 0 1 1 7 1
1 1 5 2 3 9 4 1 3 1 2 3 7 1
6 1 3 3 4 1 5 1 1 0 2 3 7 1
2 1 1 6 3 8 5 3 3 0 2 3 7 1
4 1 1 7 4 8 4 3 2 0 2 3 7 1
1 1 5 2 4 1 5 1 1 0 2 3 7 1
4 2 2 2 3 2 5 1 2 0 1 1 5 1
8 2 1 3 6 6 2 2 4 2 1 1 7 1
7 2 1 5 3 5 5 3 4 0 2 1 7 1
1 1 5 2 3 9 4 1 3 1 2 3 7 1
6 1 3 3 4 1 5 1 1 0 2 3 7 1
2 1 1 6 3 8 5 3 3 0 2 3 7 1
4 1 1 7 4 8 4 3 2 0 2 3 7 1
1 1 5 2 4 9 5 1 1 0 2 3 7 1
4 2 2 2 3 2 5 1 2 0 1 1 5 1
Following your original program logic, I come up with this version:
ldata = open("list.data", "r")
# read in all the rows, note that the list values are strings instead of integers
linelist = []
for line in ldata:
linelist.append(tuple(line.strip().split(" ")))
ldata.close()
# keep only the rows with 6th column = '1'
only1 = []
for row in linelist:
if row[5] == '1':
only1.append(row)
# tally the statistics
income_dist = {}
for row in only1:
if row[0] in income_dist:
income_dist[row[0]] += 1
else:
income_dist[row[0]] = 1
# print result
print "While column six equals '1',"
for num in sorted(income_dist):
print "the value %s appears %d times in column one." % (num, income_dist[num])