Saving and looking for next available moves - python

I'm new to coding so I apologize if the question I'm asking is quite simple.
I'm coding a game where I have to make one large piece (represented as 1's) on a board reach to the end. There are several other smaller pieces that can be moved (2 to denote horizontal 1x2 pieces, 3 to denote vertical 1x2 pieces, and 4 to denote the single pieces), along with two empty spaces (represented as 0's).
So this is how the board initially looks like coming from a txt file:
3113
3113
3223
3443
4004
2's can be moved horizontally, 3's can be moved vertically, and the single piece (4) can be moved either horizontally or vertically.
The code below simply takes the input board and converts its rows into lists:
def initialize():
row1=[]
row1[:0] = Lines[0]
del(row1[-1])
print(row1)
row2=[]
row2[:0] = Lines[1]
del(row2[-1])
print(row2)
row3=[]
row3[:0] = Lines[2]
del(row3[-1])
print(row3)
row4=[]
row4[:0] = Lines[3]
del(row4[-1])
print(row4)
row5=[]
row5[:0] = Lines[4]
print(row5)
The output of the code looks like this:
['3', '1', '1', '3']
['3', '1', '1', '3']
['3', '2', '2', '3']
['3', '4', '4', '3']
['4', '0', '0', '4']
The issue I'm having is saving this current formation and creating a list of the next possible formations
Would appreciate any help, thank you.
I can provide more clarification if needed.

The biggest problem you have is the fact you are differentiating each row of the board as a separate variable. What you need to do is define the entire board as a list of lists referenced to by a single variable. There are a couple of ways to do this:
Using your approach of a function to initialize the board.
def initialize(lines):
brd = []
for l in lines:
row = [int(x) for x in l]
brd.append(row)
return brd
The above function call returns a list of lists describing your board, so you would use it like:
lines = ['3113', '3113', '3223', '3443', '4004']
board = initialize(lines)
for l in boards:
print(l)
produces:
[3, 1, 1, 3]
[3, 1, 1, 3]
[3, 2, 2, 3]
[3, 4, 4, 3]
[4, 0, 0, 4]
If you want to output the data in a form that resembles the initial data input you can use
for l in board:
print(''.join(str(x) for x in l))
Which produces:
3113
3113
3223
3443
4004
At this point you can add functions to move pieces and return the updated board.
A better approach would be to encapsulate the board in a class definition and utilize the power of builtin functions to aide in producing your board. In this approach you would define a Board class as follows:
class Board:
def __init__(self, lines):
self.brd = []
for l in lines:
row = [int(x) for x in l]
self.brd.append(row)
def __repr__(self):
s = ""
for rw in self.brd:
s += f"{rw}\n"
return s
You would then define your board using:
board = Board(lines)
and you can display the board using
print(board)
which produces the previous, neat board display without need to cycle through the row and you can add methods within your class description for moving pieces on your board.

Related

Why am i getting a,b,c instead of actual values in output?

N = int(input())
q = []
x=list(input())
a,b,c=x[0],x[1],x[2]
q.insert(0,'a')
q.insert(0,'c')
q.insert(1,'b')
print(q)`
i want output as [6, 5, 10] how can i specifically take value from a list and insert it into another also is there a way with x not at all being a list
You need split your input and change the listName.insert values with variables.
'a' is different form a. The first is a character and the second is a variable.
q = []
x=list(input("Enter three values separated with space: ").split())
a,b,c=x[0],x[1],x[2]
q.insert(0,a)
q.insert(0,c)
q.insert(1,b)
print(q)
Output:
Enter three values separated with space:
1 2 3
['3', '2', '1']
Another thing, N is an unused variable.

How to call for information from a list?

I'm quite new to Python and I would like some help. I'm currently trying to store information from the first line of a txt file in a tuple and I am having some trouble. The txt file's second line is:
Water: 0 0 4 2 1 3
I want to store the numbers only so my current code is:
water = []
with open(file_name) as f:
lines = f.readlines()
water_values = lines[1].strip()
splitted = water_values.split(" ")
splitted.remove("Water:")
water.append(splitted)
However, when I call for water[1], expecting to receive 0, I find that the index is out of range and that the len(water) is only 1. When I print it, it says:
[['0', '0', '4', '2', '1', '3']]
How can I change it so that I can call for each element?
When you call water.append(splitted) you are adding a new element to the end of the list and splitted is a list so you get a list of lists.
If you want to combine two lists, you should instead call water += splitted. The += operator means that you are adding to the left side value, what ever is on the right side and is analogous to water = water + splitted.
You should use .extend rather than .append, i.e. instead of
water.append(splitted)
do
water.extend(splitted)
Simple example to show difference:
a = []
b = []
a.append([1,2,3])
b.extend([1,2,3])
print(a)
print(b)
output:
[[1, 2, 3]]
[1, 2, 3]
If you know to want more about handling lists in python read More on Lists in docs
your code water.append(splitted) just adds splitted (which is a list) as a first element of water list. To add values from splitted you could just do following:
water += splitted
instead of
water.append(splitted)
Doing so - you will get water = ['0', '0', '4', '2', '1', '3'].
You can read more here How do I concatenate two lists in Python?

Changing data types of elements in lists of lists

I have an imported data file.
It is a list made up of 51 lists.
Each of the nested lists has 5 elements.
I want to change 1 (The second element so location 1) of the 5 elements from string to integers.
This is what I have:
for i in range(len(statesData)):
statesData[i] = statesData[i].strip()
statesData[i] = statesData[i].split(',')
for d in statesData[i]:
d[1] = int(d[1])
d[2] = float(d[2])
Now I don't want element 1 (0) to be included since it is the heading of each category.
I want to start with 2 (1). I was thinking a range function somewhere like:
statesData[i][1:]
But that doesn't seem to work. Any advice? I should be able to do this without too much trouble. Or having to create a function.
In your second loop, you are looping over each element of the nested lists, i.e. when you're doing d[1] you are indexing the string of said element. Try this instead
for i in range(len(statesData)):
print(statesData[i])
statesData[i] = statesData[i].strip()
statesData[i] = statesData[i].split(',')
statesData[i][1] = int(statesData[i][1])
statesData[i][2] = float(statesData[i][2])
If you have a list of lists only containing integers similar to this
t = [['0', '1', '2', '3', '4'], ['0', '5', '6', '7', '8']]
you can change the type of all elements except the first for each list with a list comprehension like this:
mod_t = [[l[0]] + [int(e) for e in l[1:]] for l in t]
# [['0', 1, 2, 3, 4], ['0', 5, 6, 7, 8]]
Explanations:
You walk through all list elements of t with for l in t
For each of those lists, you'll generate a new list with the original first element l[0] and integer converted elements for all subsequent items [int(e) for e in l[1:]]
First point, semantically, your list should be a list of tuples, not a list of lists (if position is significant then it's a tuple - a list is supposed to be an homogeneous collection).
Also, changing the list in place is mostly a useless complication, specially for such a small dataset. The simplest solution is to build a new list from the existing one, ie:
def parse_row(row):
return (row[0], int(row[1]), float(row[2])) + row[3:]
def parse_data(data):
return [parse_row(row) for row in data]
if __name__ == "__main__":
state_data = [
("foo", "1", "1.5", "a", "b"),
("bar", "2", "2.5", "c", "d"),
("baaz", "3", "3.5", "e", "f"),
("quux", "4", "4.5", "g", "h"),
]
print(parse_data(state_data))
wrt/ your issue:
for d in statesData[i]:
d[1] = int(d[1])
d[2] = float(d[2])
here, statesData[i] is your current sublist, so the iteration variable d is successively rebound to each of your sublist's elements.
You could go with the following which is pretty concise:
for i, sd in enumerate(statesData):
sd = sd.strip().split(',')
if i: # omit first (index 0) from further processing
sd[1:3] = int(sd[1]), float(sd[2])
statesData[i] = sd

python add specific lists within a list

For this problem I am dealing with a big list,that it was imported from a CSV file, but let's say
I have a list like this:
[['name','score1','score2''score3''score4']
['Mike','5','1','6','2']
['Mike','1','1','1','1']
['Mike','3','0','3','0']
['jose','0','1','2','3']
['jose','2','3','4','5']
['lisa','4','4','4','4']]
and I want to have another list with this form(the sum of all score for each student):
[['Mike','9','2','10','3']
['jose','2','4','6','8']
['lisa','4','4','4','4']]
any ideas how this can be done?
I've been trying many ways, and I could not make it.
I was stuck when there where more than 2 same names, my solution only kept the last 2 lines to add.
I am new in python and programming in general.
If you are just learning Python I always recommend try to implement things without relying on external libraries. A good starting step is to start by trying to break the problem up into smaller components:
Remove the first entry (the column titles) from the input list. You don't need it for your result.
For each remaining entry:
Convert every entry except the first to an integer (so you can add them).
Determine if you have already encountered an entry with the same name (first column value). If not: add the entry to the output list. Otherwise: merge the entry with the one already in the output list (by adding values in the columns).
One possible implementation follows (untested):
input_list = [['name','score1','score2''score3''score4'],
['Mike','5','1','6','2'],
['Mike','1','1','1','1'],
['Mike','3','0','3','0'],
['jose','0','1','2','3'],
['jose','2','3','4','5'],
['lisa','4','4','4','4']]
print input_list
# Remove the first element
input_list = input_list[1:]
# Initialize an empty output list
output_list = []
# Iterate through each entry in the input
for val in input_list:
# Determine if key is already in output list
for ent in output_list:
if ent[0] == val[0]:
# The value is already in the output list (so merge them)
for i in range(1, len(ent)):
# We convert to int and back to str
# This could be done elsewhere (or not at all...)
ent[i] = str(int(ent[i]) + int(val[i]))
break
else:
# The value wasn't in the output list (so add it)
# This is a useful feature of the for loop, the following
# is only executed if the break command wasn't reached above
output_list.append(val)
#print input_list
print output_list
The above is not as efficient as using a dictionary or importing a library that can perform the same operation in a couple of lines, however it demonstrates a few features of the language. Be careful when working with lists though, the above modifies the input list (try un-commenting the print statement for the input list at the end).
Let us say you have
In [45]: temp
Out[45]:
[['Mike', '5', '1', '6', '2'],
['Mike', '1', '1', '1', '1'],
['Mike', '3', '0', '3', '0'],
['jose', '0', '1', '2', '3'],
['jose', '2', '3', '4', '5'],
['lisa', '4', '4', '4', '4']]
Then, you can use Pandas ...
import pandas as pd
temp = pd.DataFrame(temp)
def test(m):
try: return int(m)
except: return m
temp = temp.applymap(test)
print temp.groupby(0).agg(sum)
If you are importing it from a cvs file, you can directly read the file using pd.read_csv
You could use better solution as suggested but if you'd like to implement yourself and learn, you can follow and I will explain in comments:
# utilities for iteration. groupby makes groups from a collection
from itertools import groupby
# implementation of common, simple operations such as
# multiplication, getting an item from a list
from operator import itemgetter
def my_sum(groups):
return [
ls[0] if i == 0 else str(sum(map(int, ls))) # keep first one since it's name, sum otherwise
for i, ls in enumerate(zip(*groups)) # transpose elements and give number to each
]
# list comprehension to make a list from another list
# group lists according to first element and apply our function on grouped elements
# groupby reveals group key and elements but key isn't needed so it's set to underscore
result = [my_sum(g) for _, g in groupby(ls, key=itemgetter(0))]
To understand this code, you need to know about list comprehension, * operator, (int, enumerate, map, str, zip) built-ins and some handy modules, itertools and operator.
You edited to add header which will break our code so we need to remove it such that we need to pass ls[1:] to groupby instead of ls. Hope it helps.
As a beginner I would consider turning your data into a simpler structure like a dictionary, so that you are just summing a list of list. Assuming you get rid of the header row then you can turn this into a dictionary:
>>> data_dict = {}
>>> for row in data:
... data_dict.setdefault(row[0], []).append([int(i) for i in row[1:]])
>>> data_dict
{'Mike': [[5, 1, 6, 2], [1, 1, 1, 1], [3, 0, 3, 0]],
'jose': [[0, 1, 2, 3], [2, 3, 4, 5]],
'lisa': [[4, 4, 4, 4]]}
Now it should be relatively easy to loop over the dict and sum up the lists (you may want to look a sum and zip as a way to do that.
This is well suited for collections.Counter
from collections import Counter, defaultdict
csvdata = [['name','score1','score2','score3','score4'],
['Mike','5','1','6','2'],
['Mike','1','1','1','1'],
['Mike','3','0','3','0'],
['jose','0','1','2','3'],
['jose','2','3','4','5'],
['lisa','4','4','4','4']]
student_scores = defaultdict(Counter)
score_titles = csvdata[0][1:]
for row in csvdata[1:]:
student = row[0]
scores = dict(zip(score_titles, map(int, row[1:])))
student_scores[student] += Counter(scores)
print(student_scores["Mike"])
# >>> Counter({'score3':10, 'score1':9, 'score4':3, 'score2':2})
collections.defaultdict

Converting a string to a list of 2-tuples

I have strings of this shape:
d="M 997.14282,452.3622 877.54125,539.83678 757.38907,453.12006 802.7325,312.0516 950.90847,311.58322 Z"
which are (x, y) coordinates of a pentagon (the first and last letters are metadata and to be ignored). What I want is a list of 2-tuples that would represent the coordinates in floating points without all the cruft:
d = [(997.14282, 452.3622), (877.54125, 539.83678), (757.38907, 453.12006), (802.7325,312.0516), (950.90847, 311.58322)]
Trimming the string was easy:
>>> d.split()[1:-2]
['997.14282,452.3622', '877.54125,539.83678', '757.38907,453.12006', '802.7325,312.0516']
but now I want to create the tuples in a succinct way. This obviously didn't work:
>>> tuple('997.14282,452.3622')
('9', '9', '7', '.', '1', '4', '2', '8', '2', ',', '4', '5', '2', '.', '3', '6', '2', '2')
Taking the original string, I could write something like this:
def coordinates(d):
list_of_coordinates = []
d = d.split()[1:-2]
for elem in d:
l = elem.split(',')
list_of_coordinates.append((float(l[0]), float(l[1])))
return list_of_coordinates
which works fine:
>>> coordinates("M 997.14282,452.3622 877.54125,539.83678 757.38907,453.12006 802.7325,312.0516 950.90847,311.58322 Z")
[(997.14282, 452.3622), (877.54125, 539.83678), (757.38907, 453.12006), (802.7325, 312.0516)]
However this processing is a small and trivial part of a bigger program and I'd rather keep it as short and succinct as possible. Can anyone please show me a less verbose way to convert the string to the list of 2-tuples?
A note, not sure if this is intended - when you do d.split()[1:-2] , you are losing the last coordinate. Assuming that is not intentional , A one liner for this would be -
def coordinates1(d):
return [tuple(map(float,coords.split(','))) for coords in d.split()[1:-1]]
If losing the last coordinate is intentional, use [1:-2] in the above code.
You can do this in one line using list comprehension.
x = [tuple(float(j) for j in i.split(",")) for i in d.split()[1:-2]]
This goes through d.split()[1:-2]], each pair that should be grouped together, splits them by a comma, converts each item in that to a float, and groups them together in a tuple.
Also, you might want to use d.split()[1:-1] because using -2 cuts out the last pair of coordinates.
While you do all right, it's could be some compressed using list comprehension or some functional stuff (i mean "map"):
def coordinates(d):
d = d[2:-2].split() # yeah, split here into pairs
d = map(str.split, d, ","*len(d)) # another split, into tokens
# here we'd multiplied string to get right size iterable
return list(map(tuple, d)) # and last map with creating list
# from "map object"
Of couse it can be reduced into one-line with list comprehension, but readablity would be reduced too (while right now code is read hard). And although Guido hates functional programming i'm find this more logical... After some practice. Good luck!

Categories