I jumped into some Python courses a little while ago and have gotten to a milestone project to make a simple tic-tac-toe game.
But I am running into a bit of a wall due to an index error that keeps happening and I cannot figure out why.
The code is the following:
#Tic Tac Toe
game_list = [' '] * 10
turn_counter = 0
game_on = True
def show_game(game_list):
print(' | |')
print(' ' + game_list[7] + ' | ' + game_list[8] + ' | ' + game_list[9])
print(' | |')
print('-----------')
print(' | |')
print(' ' + game_list[4] + ' | ' + game_list[5] + ' | ' + game_list[6])
print(' | |')
print('-----------')
print(' | |')
print(' ' + game_list[1] + ' | ' + game_list[2] + ' | ' + game_list[3])
print(' | |')
def choose_position():
# Initial Variables
within_range = False
acceptable_values = [1,2,3,4,5,6,7,8,9]
choice = 'WRONG'
# While loop that keeps asking for input
while choice.isdigit() == False or within_range == False:
choice = input("Please choose a number between 1-9 like a numpad: ")
# DIGIT CHECK
if choice.isdigit() == False:
print("Sorry, that is not a digit!")
# RANGE CHECK
if choice.isdigit() == True:
if int(choice) in acceptable_values:
within_range = True
else:
print("Sorry, you are out of the acceptable range (1-9)")
return int(choice)
def insert_choice(game_list, position, turn_counter):
print(type(position))
print(position)
# Place the character in the game_list
if turn_counter%2 == 0 or turn_counter == 0:
game_list[position] = 'X'
else:
game_list[position] = 'O'
return (game_list, position)
def gameon_choice():
choice = 'wrong'
while choice not in ['Y', 'N']:
choice = input("Keep playing? (Y or N) ")
if choice not in ['Y', 'N', 'R']:
print("sorry, I don't understand, please choose Y or N ")
if choice == 'Y':
return True
else:
return False
while game_on:
show_game(game_list)
position = choose_position()
game_list = insert_choice(game_list,position,turn_counter)
turn_counter += turn_counter
show_game(game_list)
game_on = gameon_choice()
And the error I get is:
Exception has occurred: IndexError
tuple index out of range
File "Desktop/Tictactoe.py", line 9, in show_game
print(' ' + game_list[7] + ' | ' + game_list[8] + ' | ' + game_list[9])
File "Desktop/Tictactoe.py", line 79, in <module>
show_game(game_list)
What I think is happening is that during the assignment in the insert_choice function:
game_list[position] = 'X'
the list is somehow converted to a tuple and the variables are appended instead of assigned, and then when trying to display the list again it only has two elements leading to an index error, but I cannot figure out /why/.
I hope someone can help.
Sincerely,
The insert_choice() method returns a tuple of your game_list and position:
return (game_list, position)
Thus, during your main loop, you store this tuple as the new game_list and try to access indices greater than 1 which leads to this index error.
You can either only return game_list or unpack the returned tuple as:
game_list, position = insert_choice(game_list,position,turn_counter)
Since you don't change the value of position, you probably want to do the former.
I have to split the string with some "" in the string
I am a beginner in python, plz help me QQ
With the problem that line3 shows "index out of range"
windows
data = input().split(',')
for i in range(len(data)):
for j in range(len(data[i])):
if data[i][j] == '"':
data[i] += "," + data[i + 1]
data.pop(i + 1)
if data[i + 1][j] == '"':
data[i] += "," + data[i + 1]
data.pop(i + 1)
print(data[i])
sample input:
'str10, "str12, str13", str14, "str888,999", str56, ",123,", 5'
sample output:
str10
"str12, str13"
str14
"str888,999"
str56
",123,"
5
Your error occurs if you acces a list/string behind its data. You are removing things and access
for i in range(len(data)):
...
data[i] += "," + data[i + 1]
If i ranges from 0 to len(data)-1 and you access data[i+1] you are outside of your data on your last i!
Do not ever modify something you iterate over, that leads to desaster. Split the string yourself, by iterating it character wise and keep in mind if you are currently inside " ... " or not:
data = 'str10, "str12, str13", str14, "str888,999", str56, ",123,", 5'
inside = False
result = [[]]
for c in data:
if c == ',' and not inside:
result[-1] = ''.join(result[-1]) # add .strip() to get rid of spaces on begin/end
result.append([])
else:
if c == '"':
inside = not inside
result[-1].append(c)
result[-1] = ''.join(result[-1]) # add .strip() to get rid of spaces on begin/end
print(result)
print(*result, sep = "\n")
Output:
['str10', ' "str12, str13"', ' str14', ' "str888,999"', ' str56', ' ",123,"', ' 5']
str10
"str12, str13"
str14
"str888,999"
str56
",123,"
5
Add .strip() to the join-lines to get rid of leading/trailing spaces:
result[-1] = ''.join(result[-1]).strip()
I am a beginner at python and I am attempting to create a simple game. I am struggling to create a function that is required to take in zero arguments and returns a grid which contains randomly placed explosives.
As a general requirement, there should be a ten percent chance of getting a mine.
This is my code so far, but I'm struggling to figure out where to go from here. I also don't really understand the ten percent chance of placing a mine requirement because I thought there would have to be 10 different boxes? If someone could help push me in the right direction, I'd really appreciate it.
def mines():
gridSize = 3
createGrid = [[' ' for i in range(gridSize)] for i in range(gridSize)]
return createGrid
print(initMines())
All of these answers were really helpful, thanks! :)
Using the random library, you can use randint to get your 1 in 10 chance, and implement via an if statement
import random
GRIDSIZE = 3
CHANCE = 10
def mines():
createGrid = [[("x" if random.randint(0, CHANCE) == 0 else " ") for i in range(GRIDSIZE)] for i in range(GRIDSIZE)]
return createGrid
print(mines())
Output example
[['x', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
Edit: I have added global constants for the grid size and chance based on your question, however I would pass these as parameters if I were you.
I'm not most familiar with Python, so sorry if things don't quite work, but from the looks of things you're looking at making a 2d array, and then filling them with either an empty string "" or a mine "x", based on probability.
Based on the first answer here, you're mostly on the right track with initialising the array, though you may have to make the two "i"s different (given they represent "coordinates" in an array I'd suggest x and y)
createGrid = [[' ' for x in range(gridSize)] for y in range(gridSize)]
You then need to populate the array, and the way I'd suggest you do this would be using nested for loops as shown below:
for i in range(gridSize)
for j in range(gridSize)
createGrid[i][j] = //code for mine/empty
This will loop through all values in the array, and then update them based on whether or not it should contain a mine or be empty.
To decide whether or not it should be a mine, your best bet would probably be to import the random module, and use either the randint function or the random function, and then use an if statement to determine whether or not it should be a mine.
(The if statement goes within the for loops, the import happens before anything else in your code)
e.g.
import random
if random.randint(0, 10) <= 1
createGrid[i][j] = "x"
Hope that makes sense and is helpful!
For a 1/10 chance of a mine, you can just use something like (remembering to import random):
opts = "M........."
[[random.choice(opts) for c in range(gridSize)] for r in range(gridSize)]
It just chooses one of the characters from the string, which happens to have a 10% chance of getting a mine.
Using that in a complete program, and making it more configurable:
import random
def mines(gsz, pct):
# Silently enforce int 0-100, create choices, then choose.
pct = max(0, min(100, int(pct)))
opts = "M" * pct + ' ' * (100 - pct)
return [[random.choice(opts) for i in range(gsz)] for i in range(gsz)]
# Test harness. First, create a grid.
sz = 10
grid = mines(sz, 20)
# Then dump it for confirmation.
for line in grid: print(line)
mineCount = sum([cell == 'M' for row in grid for cell in row])
print('\nActual percentage was', 100 * mineCount / sz / sz)
shows it in action:
[' ', ' ', 'M', ' ', ' ', ' ', 'M', ' ', ' ', ' ']
['M', ' ', ' ', ' ', 'M', ' ', ' ', ' ', ' ', ' ']
['M', ' ', ' ', ' ', 'M', 'M', ' ', ' ', ' ', ' ']
[' ', 'M', 'M', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
['M', ' ', ' ', ' ', ' ', 'M', ' ', ' ', ' ', ' ']
['M', ' ', ' ', ' ', ' ', ' ', 'M', 'M', ' ', ' ']
[' ', ' ', 'M', ' ', 'M', ' ', 'M', ' ', ' ', 'M']
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', ' ', ' ', ' ', ' ', 'M', ' ', ' ', ' ']
Actual percentage was 19.0
In case you want guaranteed number of mines you could do this:
import random
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
size = int(input('Enter length of row: '))
# By default 1/size of cells will be mines.
mines = (size**2)//size
# There is probably a better way to get user input, but this will do.
try:
mines = int(input('Enter number of mines [default=%s]: ' % mines))
except:
mines = (size**2)//size
# Make an one dimensional list of size square.
field_1d = [' ']*(size**2)
# Stick the mines into the list.
for m in range(mines):
field_1d[m] = '*'
# Randomly place the mines.
random.shuffle(field_1d)
# Make a 2D list out of the 1D list.
field = [r for r in chunks(field_1d,size)]
# Display it.
for row in field:
print(row)
Here is the output:
$ ./minesweeper.py
Enter length of row: 3
Enter number of mines [default=3]: 1
[' ', ' ', ' ']
[' ', '*', ' ']
[' ', ' ', ' ']
$ ./minesweeper.py
Enter length of row: 10
Enter number of mines [default=10]:
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', '*', ' ', ' ', '*', ' ', ' ', ' ', ' ']
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
[' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', ' ']
[' ', '*', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*']
[' ', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
Sorry I could resist. I went ahead and wrote a complete minesweeper game:
import random
class Cell():
def __init__(self,i,j,field):
self.i = i
self.j = j
self.exposed = False
self.field = field
self.value = self.calc_value()
def display(self):
if self.exposed:
return self.value
return '_'
def expose(self):
self.exposed = True
def calc_value(self):
i = self.i
j = self.j
f = self.field
if self.field[i][j] == '*':
return '*'
v=0
try:
if f[i-1][j-1] == '*':
v += 1
except:
pass
try:
if f[i-1][j] == '*':
v += 1
except:
pass
try:
if f[i-1][j+1] == '*':
v += 1
except:
pass
try:
if f[i][j-1] == '*':
v += 1
except:
pass
try:
if f[i][j+1] == '*':
v += 1
except:
pass
try:
if f[i+1][j-1] == '*':
v += 1
except:
pass
try:
if f[i+1][j] == '*':
v += 1
except:
pass
try:
if f[i+1][j+1] == '*':
v += 1
except:
pass
return str(v)
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
size = int(input('Enter size of field: '))
# 1/10th of cells will be mines.
mines = (size**2)//size
try:
mines = int(input('Enter number of mines [default=%s]: ' % mines))
except:
mines = (size**2)//size
# Make an one dimensional list of size square.
field_1d = [' ']*(size**2)
# Stick the mines into the list.
for m in range(mines):
field_1d[m] = '*'
# Randomly place the mines.
random.shuffle(field_1d)
# Make a 2D list out of the 1D list.
field = [r for r in chunks(field_1d,size)]
# Display it.
for row in field:
print(row)
board_1d = []
for i in range(size):
for j in range(size):
print(i,j)
board_1d.append(Cell(i,j,field))
board = [r for r in chunks(board_1d,size)]
def display(board):
for i in range(size):
for j in range(size):
print(board[i][j].display(), end='|')
print("")
def win(board):
unexposed = 0
for i in range(size):
for j in range(size):
if board[i][j].exposed == False:
unexposed += 1
if unexposed == mines:
print('WINNER!!!!')
return True
return False
gameover = False
while not gameover:
display(board)
I = int(input('Enter I: '))
J = int(input('Enter J: '))
c = board[I][J]
c.expose()
if c.value == '*':
print("BOOM!")
gameover = True
gameover = win(board)
display(board)
I'm doing a task for school where i need my program to do four things:
(1) It should take in a filename as an argument and count lines, words and chars.
(2) It should accept the argument *.py and scan all the .py files in current directory for the same as in (1)
(3) It should accept the argument * and scan ALL files in current directory for the same as in (1).
(4) I should be able to call it from command line as simply filename followed by argument. Example: Filename is Hello_world.py and takes one argument. Then it should look like this:
Hello_world arg
When I call with a specific filename as in (1) it seems to be working here:
else:
a, b, c, n = counting(call)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
but not working for (2) and (3) here:
if call == '*':
import os
for fname in os.listdir(os.getcwd()):
a, b, c, n = counting(fname)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
elif call == '*.py':
import glob
for fname in glob.glob('*.py'):
a, b, c, n = counting(fname)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
I tried printing out sys.argv[1] when using * and *.py as argument and then it prints out the first file in the directory and not * or *.py. Why is python interpreting * as a filename in the directory instead of simply the string *? How do I work around this? I tried moving the imports untill it actually entered the if/elif, but that did no change.
When it comes to making the script callable as in (4) I'm clueless, but that is not my main concern at this moment.
Entire script here:
import sys
def counting(fname):
lines = words = chars = 0
f= open(fname)
for line in f:
lines += 1
words += len(line.split())
chars += len(line)
name = f.name
f.close()
return lines, words, chars, name
def main():
call = sys.argv[1]
print(sys.argv[1])
a = b = c = 0
if call == '*':
import os
for fname in os.listdir(os.getcwd()):
a, b, c, n = counting(fname)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
elif call == '*.py':
import glob
for fname in glob.glob('*.py'):
a, b, c, n = counting(fname)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
else:
a, b, c, n = counting(call)
print('There are: ' + str(a) + ' lines, ' + str(b) + ' words and ' + str(c) + ' chars in ' + n)
if __name__ == '__main__':
main()
All help greatly appreciated. Thanks in advance!
When you run a program like:
prog *
there's no guarantee that the * will make it through to the program unscathed.
In fact, many shells will explicitly doe expansion so that the * gets turned into a number of arguments (such as every file in the current directory, for example).
In fact, you have to go out of your way to prevent this, by escaping special characters so the shell won't interpret them, such as:
prog \*
But. if you want to process all the files anyway, you should probably let the shell do what it wants, then process each of the expanded arguments separately.
In Python, that would be something like:
import sys
for arg in sys.argv[1:]: # [0] is the program name.
print(arg)