How to have a static board on tic tac toe? - python

I was trying to build a tic-tac-toe programme, however, I want to understand how to have a static board when user inputs "O" or "X" or when I just want to see the board.
Edit: Outputs appear weird in the question, the first one is a wonky board since it has inputs, whereas the 2nd example is clean.
e.g.
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O',
'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}
def printBoard(board):
print(board['top-L'] + ' |' + board['top-M'] + ' |' + board['top-R'])
print('-+-+-')
print(board['mid-L'] + ' |' + board['mid-M'] + ' |' + board['mid-R'])
print('-+-+-')
print(board['low-L'] + ' |' + board['low-M'] + ' |' + board['low-R'])
output:
O |O |O
-+-+-
X |X |
-+-+-
| |X
2nd e.g.
theBoard = {'top-L': '', 'top-M': '', 'top-R': '',
'mid-L': '', 'mid-M': '', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ''}
def printBoard(board):
print(board['top-L'] + ' |' + board['top-M'] + ' |' + board['top-R'])
print('-+-+-')
print(board['mid-L'] + ' |' + board['mid-M'] + ' |' + board['mid-R'])
print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])
output:
| |
-+-+-
| |
-+-+-
| |
So is there a way to make it aesthetically pleasing all the time? or I have to use another model?
Further issues:
You can see that in the last line inside def on the 2nd example there's no space in the '|' which i needed to adjust, however on the first 2 '|' I needed to put a space ' |' which I have no idea why.

If you are going to output an extra space next to each vertical line, your horizontal lines need to account for that. I would actually add a blank space on both sides of the marker.
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O',
'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}
def printBoard(board):
print(' {} | {} | {} '.format(board['top-L'], board['top-M'], board['top-R']))
print('---+---+---')
print(' {} | {} | {} '.format(board['mid-L'], board['mid-M'], board['mid-R']))
print('---+---+---')
print(' {} | {} | {} '.format(board['low-L'], board['low-M'], board['low-R']))
Then
>>> printBoard(theBoard)
O | O | O
---+---+---
X | X |
---+---+---
| | X
You also need to remember that each element of the board must be a single character, either X, O, or a space.

wrapping each element with extra space seem to help the presentation a bit.
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O',
'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}
separator = '+'.join(('---',)*3)
def printBoard(board):
print(' ' + ' | '.join([board['top-L'], board['top-M'], board['top-R']]))
print(separator)
print(' ' + ' | '.join([board['mid-L'], board['mid-M'], board['mid-R']]))
print(separator)
print(' ' + ' | '.join([board['low-L'], board['low-M'], board['low-R']]))
printBoard(theBoard)

I think you have a fundamental design problem here. The data structure:
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O',
'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}
is extremely awkward to manipulate, even with Python 3.6 dict ordering and in spite of (or because of) its semantically named keys. The printing problem you're facing seems to be a side effect of this awkward representation that requires "knowing" all of the magic string keys for each square, so it's pretty much impossible to iterate over it by rows and columns or index into it naturally with 0..2 numbers for rows and columns.
While fixing your printing is the topic of the question, I suspect you have other code that might be pretty ungainly elsewhere in your application.
Consider using a data structure like a 2d list to represent the board. Here's how this might work as a very simple sketch (no error handling or win checking has been added, but this design will be more conducive to writing those things than the current approach):
class TTTBoard:
def __init__(self):
self.board = [[None] * 3 for _ in range(3)]
self.sides = "XO"
self.ply = 0
def move(self, row, col):
if not self.board[row][col]:
self.board[row][col] = self.sides[self.ply%2]
self.ply += 1
return True
def __str__(self):
return "\n---+---+---\n".join(" " + " | ".join(x if x else " " for x in row)
for row in self.board)
if __name__ == "__main__":
board = TTTBoard()
moves = [[0, 1], [0, 2], [2, 1], [1, 0], [1, 1]]
for position in moves:
board.move(*position)
print(board, "\n")
Output:
| X |
---+---+---
| |
---+---+---
| |
| X | O
---+---+---
| |
---+---+---
| |
| X | O
---+---+---
| |
---+---+---
| X |
| X | O
---+---+---
O | |
---+---+---
| X |
| X | O
---+---+---
O | X |
---+---+---
| X |
For the sake of answering your question and for comparison, you could "gather" the board in your original representation into a 2d list, then use the same nested join as above:
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O',
'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}
vals = list(theBoard.values())
board = [[vals[i+j*3] for i in range(3)] for j in range(3)]
print("\n---+---+---\n".join(" " + " | ".join(row) for row in board))
This works in Python 3.6+, but having to do this transformation, or indexing into the dictionary again and again by name for each square is less appealing than choosing a usable data structure from the start.

Related

Python 3: Making a new line in a variable

I'm currently trying to make an ASCII map via python using segments of walls. I got it to work in print() but I'm a bit new and don't know how to store it to a variable.
If I invoke the variable, it prints the list with no line breaks. If I assign a variable to a print, it works, but prints it all first before I want it to.
Any solutions? Can I format the initial a1_map list to create the new lines?
Here's what I have:
north_exit = ' ___| |___'
north_no_exit = ' ___________'
south_exit = ' ```| |``` '
mid_no_exit = '| |'
top_WandE_exit = '\u2143 L'
top_W_exit = '\u2143 |'
top_E_exit = '| L'
mid_WandE_exit = ' (\u00B0.\u00B0) '
mid_W_exit =' (\u00B0.\u00B0) |'
mid_E_exit ='| (\u00B0.\u00B0) '
lower_W_exit = '\u02E5 |'
lower_WandE_exit = 'T T'
lower_E_exit = '| T'
current_room = 'the crew quarters'
a1_map = print(north_no_exit,'\n' + top_E_exit,'\n' + mid_E_exit,'\n' + lower_E_exit, '\n' +south_exit, '\n'+ 'You are in',current_room+'.')
I could just type:
print(north_no_exit,'\n' + top_E_exit,'\n' + mid_E_exit,'\n' + lower_E_exit, '\n' +south_exit, '\n'+ 'You are in',current_room+'.')
each time. However I want to format it so I can just type:
print(a1_map)
As an example, the list output without print() looks like:
(' ___________', '\n| L', '\n| (°.°) ', '\n| T', '\n | | ', '\nYou are in', 'the crew quarters.')
I would like it to look like (as an example):
___________
| L
| (°.°)
| T
```| |```
(EDIT: Sorry, trying to make it look like a box but I cant even do it on here!)
So, I feel dumb.
Formatting it and then using a loop seemed to work
a1_map = [north_no_exit, top_E_exit, mid_E_exit, lower_E_exit, south_exit]
for segment in a1_map:
print(segment)
You can simply use + to join strings or string join() method to join them with a separator string.
You can also make a dictionary or a list to help you manage them and iterate them
I think you can use Python's f-string.
north_exit = ' ___| |___'
north_no_exit = ' ___________'
south_exit = ' ```| |``` '
mid_no_exit = '| |'
top_WandE_exit = '\u2143 L'
top_W_exit = '\u2143 |'
top_E_exit = '| L'
mid_WandE_exit = ' (\u00B0.\u00B0) '
mid_W_exit =' (\u00B0.\u00B0) |'
mid_E_exit ='| (\u00B0.\u00B0) '
lower_W_exit = '\u02E5 |'
lower_WandE_exit = 'T T'
lower_E_exit = '| T'
current_room = 'the crew quarters'
a1_map = f"{north_no_exit}\n{top_E_exit}\n{mid_E_exit}\n{lower_E_exit}\n{south_exit}\n"
message = "You are in',current_room."
print(a1_map+message)

How to make a function that lays mines to random squares on nested list?

The field is created like this:
field = []
for row in range(10):
field.append([])
for col in range(15):
field[-1].append(" ")
Tuples represent free squares where mines can be layed
free = []
for x in range(15):
for y in range(10):
free.append((x, y))
I have to lay the mines trough this function:
def lay_mines(field, free, number_of_mines):
for _ in number_of_mines:
mines = random.sample(free, number_of_mines)
field(mines) = ["x"]
I was thinking using random.sample() or random.choice(). I just can't get it to work. How can I place the string "x" to a certain random coordinate?
import random
def lay_mines(x, y, number_of_mines=0):
f = [list(' ' * x) for _ in range(y)]
for m in random.sample(range(x * y), k=number_of_mines): # random sampling without replacement
f[m % y][m // y] = 'X'
return f
field = lay_mines(15, 10, 20)
print(*field, sep='\n')
Prints:
['X', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', ' ', ' ', 'X', ' ', ' ', ' ', 'X', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'X', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', 'X', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', 'X', ' ', ' ', ' ', ' ', 'X', 'X', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', ' ', ' ', ' ', ' ', 'X', ' ', ' ', ' ', ' ', 'X', ' ', ' ', ' ']
[' ', 'X', ' ', ' ', 'X', ' ', ' ', ' ', ' ', 'X', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', 'X', 'X', 'X', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', ' ', ' ', ' ', 'X', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'X']
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'X', ' ', 'X', ' ', ' ', ' ', ' ']

While and continue issue [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
could you please help me to understand what i'm doing wrong.
I have two lists below and i need if x will be !=' ' in board => continue to input x until it will be ' '. Thank you in advance.
board = [' ', ' ', 'X', ' ', ' ', ' ']
testlist_1 = ['x','y', 'z']
x = int(input('>'))
while True:
if board[x] == ' ':
for i in range (len (testlist_1)):
board[x] = testlist_1[i]
x += 1
if board[x] !=' ':
break
else:
x = int (input ('>'))
continue
#I need:
#input:x = 2
#board = [' ', ' ', 'X', ' ', ' ', ' ']
#ask x
#input:x =0
#board = ['x', 'y', 'X', ' ', ' ', ' ']
give this a try, I added a few print statements so you can see what the board looks like each loop:
board = [' ', ' ', 'X', ' ', ' ', ' ']
testlist_1 = ['x','y', 'z']
while True:
x = int(input('>'))
if board[x] == ' ':
for test_item in testlist_1:
board[x] = test_item
x += 1
if board[x] !=' ':
break
break
print(board)
print(board)
IO:
>2
[' ', ' ', 'X', ' ', ' ', ' ']
>0
['x', 'y', 'X', ' ', ' ', ' ']

How do I make a dictionary that stores data as a list? [More complicated]

I have a problem with dictionaries that I need help with.
Here is an excerpt from weather.txt:
Hail : 1 : xxx
Hail : 2 : xxx
Hail : 3 : xxx
Rain : 1 : xxx
Rain : 2 : xxx
Rain : 3 : xxx
The first value is the weather, the second value is the intensity and the third is just a short description.
Here is an excerpt from my game:
weather = open("weather.txt")
weather_list = {}
for line in weather:
line = line.rstrip().split(":")
weather_list[line[0][int(line[1]) -1]] = (line[0], line[1], line[2])
for key, value in weather_list.items():
print key, ":", value
That prints this:
a : ('Rain ', ' 2 ', ' xxx')
i : ('Rain ', ' 3 ', ' xxx')
H : ('Hail ', ' 1 ', ' xxx')
R : ('Rain ', ' 1 ', ' xxx')
But I want it to print this:
'Rain': [('Rain ', ' 1 ', ' xxx'), ('Rain ', ' 2 ', ' xxx'), i : ('Rain ', ' 3 ', ' xxx')]
'Hail': etc...
I know my issue is with the syntax "weather_list[line[0][int(line[1]) -1]]". What I want it to do is have 1 key for each weather, and each value to be a tuple or list containing all the values for that weather, sorted by intensity, (intensity 1, intensity 2, intensity 3).
Any and all help is appreciated. Hope I explained it better this time.
Solution
This works:
weather = {}
with open('weather.txt') as fobj:
for line in fobj:
data = line.strip().split(':')
weather.setdefault(data[0], []).append(tuple(data))
weather = {key: sorted(value, key=lambda x: x[1]) for key, value in weather.items()}
The result looks like this:
>>> weather
{'Hail ': [('Hail ', ' 1 ', ' xxx'),
('Hail ', ' 2 ', ' xxx'),
('Hail ', ' 3 ', ' xxx')],
'Rain ': [('Rain ', ' 1 ', ' xxx'),
('Rain ', ' 2 ', ' xxx'),
('Rain ', ' 3 ', ' xxx')]}
Variation
The above solution has, as requested, redundant information for Rain and Hail. This version does not store them as first element in tuple but only as key in the dictionary:
weather = {}
with open('weather.txt') as fobj:
for line in fobj:
data = line.strip().split(':')
weather.setdefault(data[0], []).append(tuple(data[1:]))
weather = {key: sorted(value) for key, value in weather.items()}
The sorting is simpler and the result looks like this:
>>> weather
{'Hail ': [(' 1 ', ' xxx'), (' 2 ', ' xxx'), (' 3 ', ' xxx')],
'Rain ': [(' 1 ', ' xxx'), (' 2 ', ' xxx'), (' 3 ', ' xxx')]}

Reading row elements in a new array in python

I am converting code to write a function from a different data type.
The original code was:
note_inf_track = np.array([(n.note, n.onset/div, n.duration/div, n.velocity, n.channel, track_nr)
for n in m.tracks[track_nr].notes],
dtype = [('pitch', np.int),
('onset', np.float),
('duration', np.float),
('velocity', np.int),
('channel', np.int),
('track', np.int)])
Now my input data is a 2-dimensional list, I am not working with notes anymore.
for line in lines:
#print("b");
element = [];
for x in line.split(','):
element.append(x.strip('\r\n'));
elements.append(element);
note_inf_track = np.array([(round((round(np.asarray(elements[2], dtype="float")))), (round(np.asarray(elements[0], dtype="float"))),(round(np.asarray(elements[:][1], dtype="float"))))],
dtype = [('pitch', np.int),
('onset', np.float),
('duration', np.float)])
I am struggling to add the columns at once.
elements[2] seems to give me the row instead of the column. I can't seem to replace the for loop. Maybe my syntax is all off, I am used to java and c++, fairly new to Python.
--Update--
Based on Tarun Gaba's answer, I tried this:
note_inf_track = np.array([((round(el[2])), float(el[0]),float(el[1])) for el in elements],
dtype = [('pitch', np.int)
('onset', np.float),
('duration', np.float)]);
Gives me an error:
note_inf_track = np.array([((round(el[2])), float(el[0]),float(el[1])) for el in elements],
TypeError: a float is required
Here is the output of print(elements):
[['0.066667', ' 0.200000', ' 50.180000', ' 0.000644'], ['0.266667', ' 0.266667', ' 59.180000', ' 0.006583'], ['0.550000', ' 0.366667', ' 59.180000', ' 0.002129'], ['0.933333', ' 0.350000', ' 59.180000', ' 0.005972'], ['1.316667', ' 0.050000', ' 59.180000', ' 0.010053'], ['1.366667', ' 0.166667', ' 61.180000', ' 0.008109'], ['1.550000', ' 0.233333', ' 61.180000', ' 0.009170'], ['1.783333', ' 0.416667', ' 63.180000', ' 0.023811'], ['2.250000', ' 0.166667', ' 63.180000', ' 0.016253'], ['2.416667', ' 0.850000', ' 64.180000', ' 0.019314'], ['3.300000', ' 0.116667', ' 64.180000', ' 0.018684'], ['3.433333', ' 0.133333', ' 64.180000', ' 0.016786'], ['3.583333', ' 0.333333', ' 63.180000', ' 0.008623'], ['4.816667', ' 0.383333', ' 63.180000', ' 0.036858'], ['5.200000', ' 0.166667', ' 61.180000', ' 0.006060'], ['5.366667', ' 0.366667', ' 63.180000', ' 0.010417'], ['5.783333', ' 0.333333', ' 63.180000', ' 0.008371'], ['6.116667', ' 0.383333', ' 64.180000', ' 0.007488'], ['6.533333', ' 0.233333', ' 64.180000', ' 0.014582'], ['6.766667', ' 0.333333', ' 63.180000', ' 0.004457'], ['7.533333', ' 0.516667', ' 61.180000', ' 0.004700'], ['8.050000', ' 0.316667', ' 63.180000', ' 0.006959'], ['8.366667', ' 0.300000', ' 64.180000', ' 0.013522'], ['8.666667', ' 0.166667', ' 63.180000', ' 0.008083'], ['8.833333', ' 0.150000', ' 64.180000', ' 0.010620'], ['8.983333', ' 0.250000', ' 63.180000', ' 0.004493'], ['9.233333', ' 0.116667', ' 64.180000', ' 0.012834'], ['9.350000', ' 0.333333', ' 63.180000', ' 0.005321'], ['9.716667', ' 0.300000', ' 64.180000', ' 0.006902'], ['10.033333', ' 0.183333', ' 63.180000', ' 0.002515'], ['10.216667', ' 0.133333', ' 62.180000', ' 0.005928'], ['10.350000', ' 0.600000', ' 63.180000', ' 0.004920'], ['10.950000', ' 0.133333', ' 64.180000', ' 0.006754'], ['11.083333', ' 0.116667', ' 63.180000', ' 0.003831'], ['11.200000', ' 0.316667', ' 62.180000', ' 0.002493']]
elements is a list of lists here.
To access 3rd column(as what you seem to be trying by elements[2]), you need to do something like this:
elements = [[1,2,3], \
[4,5,6], \
[7, 8, 9]]
column = [i[2] for i in elements]
print column
#[3,6,9]
For your case, It should be something on the lines of:
np.array([el[2] for el in elements], [float(el[0]) for el in elements], [float(el[1])) for el in elements], dtype= .....
The problem is that your data is read as list of strings.
Modify your code from:
element.append(x.strip('\r\n'));
To:
element.append(float(x.strip('\r\n')));
To have your data as floats. You could also use round(float(...)) if you need rounded data.
Then put the data into a numpy array:
>>> import numpy as np
>>> data = np.array(elements)
And access to the columns as data[:, column_idx], e.g. for column 3:
>>> data[:, 2]

Categories

Resources