Python Connect 4 Game- Print X on Board - python

I am trying to create a connect four game but I cannot figure out how to print the checkers on the board. I need it to print the X at the lowest available spot in the column that the user inputted. Each time I run the program, I get the same blank 6 by 7 board. Please help. thank you!
#ConnectFour
numRows=6
numCols=7
numPlayers=2
board=[]
checkers=['X','O']
turn=0
win = False
for row in range(numRows):
tempList=[]
for col in range(numCols):
tempList.append('.')
board.append(tempList)
while not win:
turn = (turn+1)%numPlayers
userCol = input ("Player"+str(turn+1)+"please enter your col: ")
while not userCol.isdigit() or not int(userCol) in range(numCols):
userCol = input ("Player"+str(turn+1)+"please enter your col: ")
for row in range(numRows):
for col in range(numCols):
print(board[row][col], end='')
print()
for repeat in range(numRows-2):
for row in range(numRows,-1,-1):
for col in range(numCols):
if board[row][int(userCol)]==".":
board[row+1][int(userCol)]="X"
print
break
#print board
for row in range(numRows):
for col in range(numCols):
print(board[row+1][userCol],end=' ')
print()
print ("Player", checkers[turn],"has won the game.")

Here's what I noticed:
Lines 24-27: This will print the unmodified board after a player drops a checker; perhaps this should be after lines 29-35 instead?
Line 30: Looks like an off-by-one error, you might want range(numRows - 1, -1, -1) or reversed(range(numRows)) instead.
Line 29: Why is it necessary to repeat the empty-space check?
Line 31: What does this do? It iterates over each column, sure, but why is that necessary? You already know what column was requested, so why go over each column again?
Lines 32-33: Seems to read "if the space is empty, place a checker in the slot below it". I'm thinking you probably want board[row][int(userCol)] = 'X' instead
Line 41: Looks like another off-by-one error, row+1 should be row. Also, should userCol just be col? Otherwise it'll only be printing one column instead of the entire final board.
General remarks:
Generally you can achieve more concise and readable python code by avoiding the for i in range(x): do_something(mylist[i]) anti-pattern and instead using python-style iteration: for item in mylist: do_something(item).
I would re-write lines 29-35. But before you do so, ask yourself exactly what those lines are supposed to do. If I understand the task correctly, you should only need one for loop there.
Ninja edit: Also, once that's working, don't forget to handle the case that the player entered a valid column number, yet that column is completely filled, in which case you probably want to ask the same player to choose a new column.

Related

How to prevent random function printing the same row more than once from xslx file in python

So I'm a python beginner and don't really know the gist of random functions. The following is my code (but only partially so it may not make sense but it has the point of what I wanted to ask. I put the conclusion of my code on the beginning of the paragraph after the code though. P.S. the code works):
if (1 <= numb_quiz <= 100):
from openpyxl import Workbook, load_workbook
book = load_workbook('Quiz_Problems.xlsx')
read_sheet = book['100 Questions']
length_row = read_sheet.max_row
sheet = book.active
numb_question = 1
point = 0
engagement = 0
correct = 0
while numb_question <= numb_quiz:
randrow = random.randint(2, length_row)
for row in read_sheet.iter_rows(randrow, randrow, min_col = 2, max_col = 2):
print("")
print("Question number", str(numb_question) + ":")
print([cell.value for cell in row])
for col in read_sheet.iter_rows(min_row = randrow, max_row = randrow, min_col = 3, max_col = 3):
print([cell.value for cell in col])
break
guest_answer = str(input("answer: "))
answer = str("D"+str(randrow))
correct_ans = sheet[answer].value
As you can see, to conclude what I wanted to do with the code above is to print random rows in column 3 with the random function from xslx file using python. The thing is, I wanted to prevent the random function to print the row from column 3 more than once in one run. Since again, I don't really understand what the 'import random' library can actually do, perhaps there is a way to get what I wanted. Does anyone have a solution? thank you so much
P.S. Since I'm a python beginner, I also would fancy an explanation from you (so not just the code. thank you!)
So random just generates a number in that range you want. Computers don't generate numbers totally randomly. They use a "seed" to get started. If you pass the library the same seed each time (set it via random.seed(seedNo)), it will generate the same numbers in the same order. This can be useful for testing but also defeat the purpose of generating numbers. Therefore, a common practice is getting the current time of the system and using that as the seed.
Now for making sure you don't get the same question printed twice, I recommend you create a new empty list and each time you get a question, you add that index to the list. Then, put in a conditional checking if the index has come up before, and if it has, just pass (move on to the next iteration of the loop). The code might look like this:
askedAlready = []
while numb_question <= numb_quiz:
randrow = random.randint(2, length_row)
if randrow in askedAlready: continue
for row in read_sheet.iter_rows(randrow, randrow, min_col = 2, max_col = 2):
print("")
print("Question number", str(numb_question) + ":")
print([cell.value for cell in row])
for col in read_sheet.iter_rows(min_row = randrow, max_row = randrow, min_col = 3, max_col = 3):
print([cell.value for cell in col])
break
guest_answer = str(input("answer: "))
answer = str("D"+str(randrow))
correct_ans = sheet[answer].value
askedAlready.append(randrow)
You could also rethink how you make the random generation. Make a list of all the possible question numbers then shuffle it. It won't have any duplicates. Then you could just iterate through the shuffled list. Note that this also uses the seeding concept in the same way (random.seed())
(Inspired by thefourtheye's answer: Generate 'n' unique random numbers within a range)
listOfRandRows = list(range(2,length_row))
random.shuffle(listOfRandRows) #this will shuffle the list "in place"
#then iterate
for i in listOfRandRows:
#do your thing. i will be the row number randomly chosen
Edit per pjs's recommendation: shuffling is efficient enough but rejection can take a lot of extra time. Sample is better than rejection. It's described here: Generate 'n' unique random numbers within a range under Two-Bit Alchemist's answer

After a while loop has finished, the code doesn't continue after that

I am trying to make a quiz, in python, where I use quite a few while loops, so I can easily leave code, or re run it. The problem is, once one of my nested loops has finished running, the code doesn't continue to run. I will leave some pseudocode incase my logic is incorrect, and the actual code after that.
Pseudocode
i = 0
array = [0]
while i < 1:
while length of array < 11:
do something
print "done something!"
Basically, once the length of array has reached 11, the print doesn't happen.
Here is the actual code also
diff =int(input("Choose a difficulty; 1 2 3"))
diffs = [1, 2, 3]
while diff not in diffs:
diff = int(input("Invalid input; choose a difficulty; 1 2 3"))
data = []#This will hold all of the data in the quiz file
answers = []#This will hold the current answers available to the user
answers2 = []#This will hold the current questions used, so that the answers do appear in a random order
letters = ["A. ","B. ","C. ","D. "]#This is so that each answer has an identifier
Questions = [0]
i = 0
score = 0
with open(filenameq,"r") as quizFile:
fileReader = csv.reader(quizFile)
for row in fileReader:
data.append(row)#This creates a 2D array, so that the program can access specific values in the file
while i < 1:
while len(Questions) < 11:
a = 0
while a in Questions:
a = randint(0,9)
Questions.append(a)
print(data[0][a])#The cell where the question is located in the file
answers = []
answers2 = []
for x in range(0,(diff+1)):
answers.append(data[a+1][x])
x = 0
b = 0
correct = 0
while x <= diff:
b = randint(0,diff)
if b in answers2:
continue
answers2.append(b)
print(letters[x]+answers[b])
if b == 0:#the first item in the CSV file is the correct value
correct = x
x += 1
answer = input("Enter the letter of the answer you think is correct").upper()
if correct == letters.index(str(answer[0]+". ")):#This is the index of the letter that the user entered, in the letters list
score += 1
print("Quiz finished")
with open(filename,"a+") as scoreFile:
fileWriter = csv.writer(scoreFile)
fileReader = csv.reader(scoreFile)
for row in fileReader:
if row[0] == username:
print(row)
row[2] = "y"
print(row)
fileWriter.writerow(row)
Finally, here is the csv file i am trying to manipulate
What is the name of the gorgon in Greek Mythology?,How did a warrior defeat Medusa in Greek Mythology,Who is the God of the Yellow River in Chinese Mythology?,Who is the mother of Hephaestus in Greek Mythology?,Who is the mother of Hephaestus in Greek Mythology?,Which river was Achilles dipped in as a baby?,Where did Zeus cast the Titans after Cronus defeated them?,What does the Helm of Darkness grant to the wearer?,Which is a pantheon of Norse Gods - excluding the Aesir?,What is Yggdrasil?
Perseus,Medusa,Ares,Zeus
A Shield,A Virus,Laceration,Cavalry
He Bo,Yang Zing,Kukulkan Ah Puch
Hera,Aphrodite,Demeter,Persephone
Pomegranate,Orange,Guava,Apple
Styx,Cocytus,Acheron,Phlegethon
Tartarus,The Asphodel Meadows,The Underworld,The Mourning Fields
Invisibility,Invincibility,Immortality,Ignitability
Vanir,Hel,Tyr,Yggdrasil
A Plant,A Person,A Deity,A World
So, each question is at the top, and the possible answers are in the bottom for each question, with the correct answers as row[0], or the first index in each line.
Thank you in advance for helping me :)
EDIT: Added some extra code to my main code, that I forgot to include originally, to clarify what "diff" and "diffs" are
Seems to be due to the fact that you never close your initial while loop: while i < 1. Since the value of i stays at 0, your outermost while loop will never close, causing your program to be stuck in an infinite loop. If you close that loop by setting i = 1 at the end, this particular problem should be resolved.
Alright, I figured it out myself, and it is exactly what I thought it was; the logic in the 1st nested while loop prevented it from ending, as the Questions array was Questions = [0], and the a variable was prevented from being in Questions if a was already in Questions, but a could already be 0-9, when 0 was already in Questions. Basically, It never could end!

Index Out Of Range When Artificially Limited

when I run this program, sometimes I receive an error.This error however is not possible as I am using an 8x8 grid and I limit the inputs so that they can only be numbers from 0-7, to obey the fact that list indexes start at 0.
The user must input coordinates (1-8),(A-H) and the program will check to see if those coordinates are correct, by systematically going through the CompShips list and repeatedly comparing those coordinates to ones given by the user. If the cords match, then a message will appear and a "Z" will change to an "X" on those coordinates, indicating a HIT. If the guess does not match, a "Z" will change to an "M" on those coordinates indicating a MISS.
CompShips=[[1,0],[1,1],[2,2],[2,3],[2,4],[3,0],[3,1],[3,2],[5,4],[5,5],[5,6],[5,7],[1,7],[2,7],[3,7],[4,7],[5,7]]
FRow1=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow2=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow3=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow4=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow5=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow6=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow7=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow8=["Z","Z","Z","Z","Z","Z","Z","Z",]
def PrintFireBoard():
print(Index)
print(FRow1)
print(FRow2)
print(FRow3)
print(FRow4)
print(FRow5)
print(FRow6)
print(FRow7)
print(FRow8)
FireBoard=[FRow1,FRow2,FRow3,FRow4,FRow5,FRow6,FRow7,FRow8]
while len(CompShips) !=0 or CompSuccess==17:
FireRow=input("Please Choose The Row That You Wish To Fire Upon (1-8) ")
FireIndex=input("Please Choose The Column That You Wish To Fire Upon (A-H) ")
#As Lists start at 0
FireRow=int(FireRow)-1
if FireIndex==("A"):
FireIndex=0
elif FireIndex==("B"):
FireIndex=1
elif FireIndex==("C"):
FireIndex=2
elif FireIndex==("D"):
FireIndex=3
elif FireIndex==("E"):
FireIndex=4
elif FireIndex==("F"):
FireIndex=5
elif FireIndex==("G"):
FireIndex=6
elif FireIndex==("H"):
FireIndex=7
Guess=[FireRow,FireIndex]
#Check To See If Correct
UserSuccess=0
for i in CompShips:
if Guess==i:
CompShips.remove(Guess)
UserSuccess=1
else:
pass
if UserSuccess==1:
print("HIT")
print(FireRow)
print(FireIndex)
FireBoard[[FireRow][FireIndex]]=("H")
PrintFireBoard()
else:
print("MISS")
print(FireRow)
print(FireIndex)
FireBoard[[FireRow][FireIndex]]=("M")
PrintFireBoard()
I receive the error:
IndexError: string index out of range
Looks like these two lines
FireBoard[[FireRow][FireIndex]]=("H")
FireBoard[[FireRow][FireIndex]]=("M")
should be
FireBoard[FireRow][FireIndex]="H"
FireBoard[FireRow][FireIndex]="M"
Explanation: In your old code, FireBoard[[FireRow][FireIndex]]=("H")
[FireRow][FireIndex] means, given a list [FireRow] (which contains just one element), get the FireIndex-th element. This is not what you're trying to do.
For example [3][0] returns 3, and [3][1] gives IndexError.
Take a look at How to define a two-dimensional array in Python
Also note that ("H") is the same as the string "H". There is no need to add parentheses.
Here is a much cleaner code!
CompShips=[[1,0],[1,1],[2,2],[2,3],
[2,4],[3,0],[3,1],[3,2],
[5,4],[5,5],[5,6],[5,7],
[1,7],[2,7],[3,7],[4,7],
[5,7]]
FRow=[["Z"]*8]*8 #1 More Pythonic
def PrintFireBoard():
#print(Index)
for i in range(0,8):
print(FRow[i])
FireBoard=FRow[:] #NOTE THIS ONE!!!
mydict = {}
for i,key in enumerate(["A","B","C","D","E","F","G","H"]): #2 More Pythonic
mydict[key] = i
while len(CompShips) !=0 or CompSuccess==17:
FireRow=input("Please Choose The Row That You Wish To Fire Upon (1-8) ")
FireIndex=input("Please Choose The Column That You Wish To Fire Upon (A-H) ")
FireRow=int(FireRow)-1
FireIndex = mydict[FireIndex]
Guess=[FireRow,FireIndex]
print(Guess)
UserSuccess=0
for i in CompShips:
if Guess==i:
CompShips.remove(Guess)
UserSuccess=1
else:
pass
if UserSuccess==1:
print("HIT")
print(FireRow,FireIndex)
FireBoard[FireRow][FireIndex]="H" #3 your problem here
PrintFireBoard()
else:
print("MISS")
print(FireRow,FireIndex)
FireBoard[FireRow][FireIndex]="M"
PrintFireBoard()
1) As explained in the comments that's just a more nicer way to create a list of lists!. Remember DRY principle! Do Not Repeat yourself!
2) Instead of having all that if else to convert the 'A' to 0. You can use a dictionary lookup instead!
3) Your problem seems to be here! correct this to FireBoard[FireRow][FireIndex]="H"
PS: NOTE THIS ONE!!!: I'm not just making FireBoard as an alias to FRow! I'm copying it into a FireBoard as a new list! There's a subtle difference read about it here. I'm doing this incase you don't want your original FRow list to be modified!
The indentation in your question was off. I think that all the code from
Guess=[FireRow,FireIndex]
until the end should be preceded by 4 spaces.
I've removed print(Index) since it was not defined.
To access FireBoard use:
FireBoard[FireRow][FireIndex]
Instead of
FireBoard[[FireRow][FireIndex]]
This should be working
CompShips=[[1,0],[1,1],[2,2],[2,3],[2,4],[3,0],[3,1],[3,2],[5,4],
[5,5],[5,6],[5,7],[1,7],[2,7],[3,7],[4,7],[5,7]]
FRow1=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow2=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow3=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow4=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow5=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow6=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow7=["Z","Z","Z","Z","Z","Z","Z","Z",]
FRow8=["Z","Z","Z","Z","Z","Z","Z","Z",]
def PrintFireBoard():
print(FRow1)
print(FRow2)
print(FRow3)
print(FRow4)
print(FRow5)
print(FRow6)
print(FRow7)
print(FRow8)
FireBoard=[FRow1,FRow2,FRow3,FRow4,FRow5,FRow6,FRow7,FRow8]
while len(CompShips) !=0 or CompSuccess==17:
FireRow=input("Please Choose The Row That You Wish To Fire Upon (1-8) ")
FireIndex=input("Please Choose The Column That You Wish To Fire Upon (A-H) ")
#As Lists start at 0
FireRow=int(FireRow)-1
if FireIndex==("A"):
FireIndex=0
elif FireIndex==("B"):
FireIndex=1
elif FireIndex==("C"):
FireIndex=2
elif FireIndex==("D"):
FireIndex=3
elif FireIndex==("E"):
FireIndex=4
elif FireIndex==("F"):
FireIndex=5
elif FireIndex==("G"):
FireIndex=6
elif FireIndex==("H"):
FireIndex=7
Guess=[FireRow,FireIndex]
#Check To See If Correct
UserSuccess=0
for i in CompShips:
if Guess==i:
CompShips.remove(Guess)
UserSuccess=1
else:
pass
if UserSuccess==1:
print("HIT")
print(FireRow)
print(FireIndex)
FireBoard[FireRow][FireIndex]=("H")
PrintFireBoard()
else:
print("MISS")
print(FireRow)
print(FireIndex)
FireBoard[FireRow][FireIndex]=("M")
PrintFireBoard()

Swapping two characters in a 2D array in python?

So, I'm brand new to programming, and this is frustrating me! What I want to do is be able to import a 4x8 text file, and turn the text into a 2D list so that I can swap two characters. For example, if the imported text file looks like this:
OOOOOOOO
OOOXOOOO
OOOOOOOO
OOOOOOOO
then I would like to be able to change the position of the X (the row/column location) when user input is entered, such that an O will get put in its place to preserve formatting. So, for exapmle, the program will prompt the user for their input and if the user enters "up," then the X will move one space up.
OOOXOOOO
OOOOOOOO
OOOOOOOO
OOOOOOOO
I want it to repeatedly prompt for a new move after each time one is made, and display the new grid each time (so you can see the X in its new position each time you enter a movement).
This is all I have so far. I know I need to first find the X, but I really don't know how to. I'm stuck here. Any help is appreciated.
#Global variables for the movements
UP = 8
DOWN = 2
RIGHT = 6
LEFT = 4
#Dimensions of grid
ROWS = 4
COLUMNS = 8
def start():
filename = input("Enter the name of the Start Positions file: ")
textFile = open(filename)
aMaze = [line.strip() for line in textFile]
for r in range(ROWS):
for c in range(COLUMNS):
print(aMaze[r][c], end="")
print()
def moveType():
while (True):
try:
move = input("ENTER YOUR MOVE: ")
except ValueError:
print("unimportant error message")
continue
if ((int(move)) in (DOWN, LEFT, RIGHT, UP)):
playerMove(move)
continue
else:
print("unimportant error message")
continue
return(move)
def playerMove(move):
move = (int(move))
if (move == DOWN):
#need help here
elif (move == UP):
#and here
elif (move == LEFT):
#don't know what i'm doing
elif (move == RIGHT):
#also here
start()
moveType()
This is a perfect opportunity to learn about abstraction. To solve your problem, think about the sub problems you could solve (with functions) that would make your final problem easier.
In your specific instance, wouldn't it be easier to write a program to find the Cartesian coordinates of where X is? With an (x,y) coordinate, you could then make a function to turn that coordinate (likely stored as a tuple) into a 2d array where that coordinate is an X an everything else is a zero.
Hint 1:
x =0
y =0
for row in numrows:
for col in numcols:
if array[row][col] == X
y = row
x = col
Hint 2:
for row in numrows:
for col in numcols:
if col is x and row is y:
place X
else:
place O
Note: if this were an application where you wanted to eek out every bit of performance, you certainly would not need to iterate through your array every time to find X. You could (and should) opt to store the location of X and then use two accesses into your array to flip X's and O's. But seeing as this is likely one of your first problems you are solving this is of course not a concern.
Hope this helps! Good luck starting to code!

Nested Loop 'If'' Statement Won't Print Value of Tuple

Current assignment is building a basic text adventure. I'm having trouble with the following code. The current assignment uses only functions, and that is the way the rules of the assignment state it must be done.
def make_selections(response):
repeat = True
while repeat == True:
selection = raw_input('-> ')
for i, v in enumerate(response):
i +=1 # adds 1 to the index to make list indices correlate to a regular 1,2,3 style list
if selection == i:
print v[1]
else:
print "There's an error man, what are you doing?!?!?"
firstResponse = 'You chose option one.'
secondResponse = 'You chose option two.'
thirdResponse = 'You chose option three.'
responses = [(0, firstResponse), (1, secondResponse),( 0, thirdResponse)]
make_selections(responses)
My intention in that code is to make it so if the user selects a 1, it will return firstResponse, if the user selects 2 it will return secondResponse, etc.
I am basically just bug testing the code to make sure it produces the appropriate response, hence the "Error man..." string, but for some reason it just loops through the error message without printing the appropriate response string. Why is this?
I know that this code is enumerating the list of tuples and I can call them properly, as I can change the code to the following and get the expected output:
for i, v in enumerate(response):
i += 1 # adds 1 to the index to make list indices correlate to a regular 1,2,3 style list
print i, v
Also, two quick asides before anyone asks:
I know there is currently no way to get out of this while loop. I'm just making sure each part of my code works before I move on to the next part. Which brings me to the point of the tuples.
When I get the code working, a 0 will produce the response message and loop again, asking the user to make a different selection, whereas a 1 will produce the appropriate response, break out of the loop, and move on to the next 'room' in the story... this way I can have as many 'rooms' for as long of a story as I want, the player does not have to 'die' each time they make an incorrect selection, and each 'room' can have any arbitrary amount of options and possible responses to choose from and I don't need to keep writing separate loops for each room.
There are a few problems here.
First, there's no good reason to iterate through all the numbers just to see if one of them matches selection; you already know that will be true if 1 <= selection <= len(response), and you can then just do response[selection-1] to get the v. (If you know anything about dicts, you might be able to see an even more convenient way to write this whole thing… but if not, don't worry about it.)
But if you really want to do this exhaustive search, you shouldn't print out There is an error man after any mismatch, because then you're always going to print it at least twice. Instead, you want to only print it if all of them failed to match. You can do this by keeping track of a "matched" flag, or by using a break and an else: clause on your for loop, whichever seems simpler, but you have to do something. See break and continue Statements, and else Clauses on Loops in the tutorial for more details.
But the biggest problem is that raw_input returns a string, and there's no way a string is ever going to be equal to a number. For example, try '1' == 1 in your interactive interpreter, and it'll say False. So, what you need to do is convert the user's input into a number so you can compare it. You can do that like this:
try:
selection = int(selection)
except ValueError:
print "That's not a number!"
continue
Seems like this is a job for dictionaries in python. Not sure if your assignment allows this, but here's my code:
def make_selections(response):
selection = raw_input('-> ')
print response.get(selection, err_msg)
resp_dict = {
'1':'You chose option one.',
'2':'You chose option two.',
'3':'You chose option three.'
}
err_msg = 'Sorry, you must pick one of these choices: %s'%sorted(resp_dict.keys())
make_selections(resp_dict)
The problem is that you are comparing a string to an integer. Selection is raw input, so it comes in as a str. Convert it to an int and it will evaluate as you expect.
You can check the type of a variable by using type(var). For example, print type(selection) after you take the input will return type 'str'.
def make_selections(response):
repeat = True
while repeat == True:
selection = raw_input('-> ')
for i, v in enumerate(response):
i +=1 # adds 1 to the index to make list indices correlate to a regular 1,2,3 style list
if int(selection) == i:
print v[1]
else:
print "There's an error man, what are you doing?!?!?"

Categories