Create Loop So Whole Task Repeats - loops

import sgenrand
# program greeting
print("The purpose of this exercise is to enter a number of coin values")
print("that add up to a displayed target value.\n")
print("Enter coins values as 1-penny, 5-nickel, 10-dime,and 25-quarter.")
print("Hit return after the last entered coin value.")
print("--------------------")
#print("Enter coins that add up to 81 cents, one per line.")
total = 0
#prompt the user to start entering coin values that add up to 81
while True:
final_coin= sgenrand.randint(1,99)
print ("Enter coins that add up to", final_coin, "cents, on per line")
user_input = int(input("Enter first coin: "))
if user_input != 1 and user_input!=5 and user_input!=10 and user_input!=25:
print("invalid input")
total = total + user_input
while total != final_coin:
user_input = int(input("Enter next coin:"))
total = total + user_input
if user_input == input(" "):
break
if total > final_coin:
print("Sorry - total amount exceeds", (final_coin))
if total < final_coin:
print("Sorry - you only entered",(total))
if total== final_coin:
print("correct")
goagain= input("Try again (y/n)?:")
if goagain == "y":
if goagain == "n":
print("Thanks for playing ... goodbye!" )
I've been trying to create this loop, so it can repeat the whole program at the end when the user accepts/ if he accepts to do it again at the end.
I know you have to have a while statement around your whole program, but with my while true statement at the top, it only repeats the first part of my program and not the whole thing.

Well one thing you should be careful of is that you do not set the
total = 0
at the start of each loop. So when the user plays the game again. He will continue using his previous total. You should move total = 0 to the start of the loop
while True:
total = 0
Additionally, you need to deindent you first
while True
statement as it does not align properly with the rest of your code.
Finally, you need to allow the user to exit the while loop after he selects No for trying again.
if goagain == "n":
print("Thanks for playing ... goodbye!" )
break
This can be done by applying a break statement.

Related

Ruby Array Elements

I am trying to create password Generate in ruby. At the moment all is working just got stuck at the final piece of generating the password.
I asked user if he/she would like the password to include numbers, lowercase or uppercase.
If YES, user will enter 1 and 0 for NO.
I used the code below to generate password if everything is 1. Meaning user want to include numbers, lowercase and uppercase.
if numbers == 1 && lowercase == 1 && uppercase == 1
passGen = [(0..9).to_a + ('A'..'Z').to_a + ('a'..'z').to_a].flatten.sample(10)
end
p passGen
This works 90% of the time. 10% of the time the generated password will not include say any numbers. But everything else present. I am not sure if this is because of the size or length of Array from which the password is sampled.
Anyway lets go to the main problem below
Here is the problem, I am struggling to write the code to generate password if one or more of input is 0. That's if user don't want to include numbers. Or no numbers and uppercase etc . As I can't predict what user may want or not want. I need help on this please.
Thank you.
You will need to make your input array more dynamic:
passGen = []
passGen += (0..9).to_a if numbers == 1
passGen += ('A'..'Z').to_a if uppercase == 1
passGen += ('a'..'z').to_a if lowercase == 1
passGen.sample(10).join
Now, to tackle your other issue with missing characters - this is caused as you are simply taking 10 random characters from an array. So it can just take, for example, all digits.
To tackle this you need to get one character from each generator first and then generate the remaining characters randomly and shuffle the result:
def generators(numbers:, lowercase:, uppercase:)
[
(0..9 if numbers),
('A'..'Z' if uppercase),
('a'..'z' if lowercase)
].compact.map(&:to_a)
end
def generate_password(generators:, length:, min_per_generator: 1)
chars = generators.flat_map {|g| Array.new(min_per_generator) { g.sample }}
chars += Array.new(length - chars.length) { generators.sample.sample }
chars.shuffle.join
end
gens = generators(numbers: numbers == 1, uppercase == 1, lowercase: lowercase == 1)
Array.new(10) { generate_password(generators: gens, length: 10) }
The code doesn't know it needs to include a digit/letter from every group. The sample takes random signs and since you a basically sampling 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz there is a possibility that all the signs will not be digits.
The easiest way to fix it is to check if a sign from every group is in the "password" and then replace a random sign with a sign from group that is not present.
If I were to program this I would do it like that
def random_from_range(range)
range.to_a.sample.to_s
end
def passGen(numbers, lowercase, uppercase)
result = ''
possibleSigns = []
if numbers == 1
range = (0..9)
result += random_from_range(range)
possibleSigns += range.to_a
end
if lowercase == 1
range = ('A'..'Z')
result += random_from_range(range)
possibleSigns += range.to_a
end
if uppercase == 1
range = ('a'..'z')
result += random_from_range(range)
possibleSigns += range.to_a
end
desired_lenth = 10
while result.length < desired_lenth
result += possibleSigns.sample.to_s
end
result
end
puts passGen(1,1,1)
By saying (0..9).to_a + ('A'..'Z').to_a + ('a'..'z').to_a, you're creating an Array of 10 + 26 + 26 = 62 elements, and then you pick only 10 elements out of it.
At your place I'd wrap password generation around an until block:
def generate_password_with_digits_and_caps
[(0..9).to_a + ('A'..'Z').to_a + ('a'..'z').to_a].flatten.sample(10).join
end
passGen = ''
until passGen.match(/[A-Z]/) && passGen.match(/[a-z]/) && passGen.match(/\d/)
passGen = generate_password_with_digits_and_caps
end
This could also work (closer to your snipppet):
if numbers == 1 && lowercase == 1 && uppercase == 1
passGen = ''
until passGen.match(/[A-Z]/) && passGen.match(/[a-z]/) && passGen.match(/\d/)
passGen = [(0..9).to_a + ('A'..'Z').to_a + ('a'..'z').to_a].flatten.sample(10).join
end
end
Start with something simple and stupid:
passGen = (('0'..'9').to_a.sample(1)+ ('A'..'Z').to_a.sample(1)+('a'..'z').to_a.sample(8).shuffle).join
Technically speaking, this already fulfills your requirement. From the viewpoint of aesthetics and security, the disadvantage here is that the number of upper case characters is always 8. A more elegant solution would be to find three non-zero integers which add up to 10, and can be used as the arguments for the sample call. Also, if no numbers are requested, you simply pass 0 as argument to sample.
Since this exceeds the scope of your question, and I don't even know whether you want to go so far, I don't elaborate on this here further.

Skip one iteration and adding conditions Python (Basic Question)

I have been reading a text file a object and create a list with the contents.
textfile:
Activity Time Location
Football 8-9 Pitch
Basketball 9-10 Gym
Lunch 11-12 Home
Read 13-14 Library
Swim 14-15 Pool
openTime = 6
closeTime = 15
come = int(input('When do you want to come?'))
leave = int(input('When do you want to leave?'))
# endtime_of_activity and startTimeofactivity is equal to the startingtime
# of each activity and the end time of each activity in the textfile
# (taken from a list that I have been splitting).
for i in range(len(my_list)):
item = my_list[i]
if (i == 1):
continue
if closeTime <= come <= endtime_of_activity and startTimeofactivity < leave <= closeTime:
print(item.activities)
My question: As you can read in the textfile there are some activities appering on different times. For example football between 8 and 9. With the code I want to be able to skip the second element (basketball) as the code is doing, however, I want the if statement under "continue" to work. If i type that im coming 8 and leaving at 12 I want all the activities (excluding the second one) to show. This works for me when I'm doing a regular for-loop without skiping the second activity, like when im just writing: for i in my_list, then adding on the condition, but when Im doing the code above it shows me all the activites (except basketball) independeltly of when I chose to come and leave. What have I missed? How could I write the code better?
If you want to skip a certain activity, simply test if the activitiy to be printed is the same and skip it if it is:
with open("f.txt","w") as f:
f.write(("Activity Time Location\nFootball 8-9 "
"Pitch\nBasketball 9-10 Gym\nLunch 11-12"
" Home\nRead 13-14 Library\nSwim 14-15"))
data = []
with open("f.txt") as f:
for line in f:
act, time, what = (line.strip().split(" ") + ["","",""])[:3]
if data:
try:
time=list(map(int, time.split("-")))
except ValueError:
continue # invalid time: skip row
data.append([act,time,what])
header,*data = data
openTime = 6
closeTime = 15
come = 9
leave = 12
skip=set(["Basketball"])
fmt = "{:<15}"*len(header)
print(fmt.format(*header))
for act,(start,stop),what in data:
if act in skip:
continue
if start >= come and stop <= leave and openTime <= come and closeTime >= leave:
print(fmt.format(act, f"{start}-{stop}", what))
Output:
Activity Time Location
Football 8-9 Pitch
Lunch 11-12 Home

Connect Four game

In my Ruby class, I'm building a Connect Four game that my professor will run in command prompt.
It has to be done with a double array and while loops, and no break/exit/abort, loop do, classes, instance, or global variables.
My grid array is made up of 64 '.' placeholders in a nested array. I am trying to start at the bottom row of my 8x8 grid, and drop in a player's piece: 'X' or '0'.
I'm not sure how to move upward from row 8/index 7 to row 7/index 6 if the bottom row of a column has already been taken. Do I use if or case statements? Do I decrement the row? I have tried putting in if/elsif, but I got nowhere.
def print_playing_grid (playing_board)
puts "1 2 3 4 5 6 7 8"
playing_board.each do |row|
puts row.join(" ")
end
end
print_playing_grid(grid_array)
# this 'win'/while is only here for testing so the board will repeat on screen
win = false
while win == false
puts
puts "Please select a column to make your move (Player X):"
user_choice = gets.to_i
row = 7
column = user_choice - 1
while row < grid_array.size
grid_array[row][column] = 'X'
row += 1
end
puts
print_playing_grid(grid_array)
end
As it's only two conditions (cell is occupied or not) I'd be more likely to use an if rather than a case... I would use case for multiple tests as it can be a little cleaner than if and elsif.
player_token = 'X'
row = grid_array.size - 1
column = user_choice - 1
while (grid_array.size - row) <= grid_array.size
if grid_array[row][column] == '.'
grid_array[row][column] = player_token
row = -1 # (to exit the while loop)
else
row -= 1
end
end
for an example of where I'd consider using a case...
player_token = 'X'
row = grid_array.size - 1
column = user_choice - 1
dropping_a_token = true
while dropping_a_token
case
when row < 0
dropping_a_token = false
when grid_array[row][column] == '.'
grid_array[row][column] = player_token
dropping_a_token = false
else
row -= 1
end
end

Python loop to print salaries within range of the average

I'm an absolute beginner to Python and am tasked with creating a program that does a few things:
Inputs employee names into a list.
Inputs that employee's salary after inputting their name.
Totals the salaries in a list, (2 lists: names[] and salaries[]).
Finds the average salary after totaling.
Prints employees that earn within $5,000 of the average salary (Where I'm stuck).
Please see my code below:
# function to total the salaries entered into the "newSalary" variable and "salaries[]".
def totalSalaries(salaries):
total = 0
for i in salaries:
total += i
return total
# Finds the average salary after adding and dividing salaries in "salaries[]".
def averageSalaries(salaries):
l = len(salaries)
t = totalSalaries(salaries)
ave = t / l
return ave
# Start main
def main():
# Empty names list for "name" variable.
names = []
# Empty salaries list for "salary" and "newSalary" variables.
salaries = []
# Starts the loop to input names and salaries.
done = False
while not done:
name = input("Please enter the employee name or * to finish: ")
salary = float(input("Please enter the salary in thousands for " + name + ": "))
# Try/except to catch exceptions if a float isn't entered.
# The float entered then gets converted to thousands if it is a float.
try:
s = float(salary)
# Message to user if a float isn't entered.
except:
print("Please enter a valid float number.")
done = False
newSalary = salary * 1000
# Break in the loop, use * to finish inputting Names and Salaries.
if name == "*":
done = True
# Appends the names into name[] and salaries into salaries[] if * isn't entered.
# Restarts loop afterwards if * is not entered.
else:
names.append(name)
salaries.append(newSalary)
# STUCK HERE. Need to output Names + their salaries if it's $5,000 +- the total average salary.
for i in range(len(salaries)):
if newSalary is 5000 > ave < 5000:
print(name + ", " + str(newSalary))
# Quick prints just to check my numbers after finishing with *.
print(totalSalaries(salaries))
print(averageSalaries(salaries))
main()
Any info is greatly appreciated. I hope the rest of the functions and logic in this program makes sense.
Your haven't written your iterator correctly. With an array, you can just use for element in array: and the loop will iterate over array by putting each element in element. So your for loop becomes for salary in salaries.
Also, You need to split your condition in two and use additions and substractions. Your code should check if the salary is higher or equal to the average minus 5000 and if it is lower or equal to the average plus 5000. If you want to formalize this mathematically it would be:
Salary >= Average - 5000
and
Salary <= Average + 5000
So the condition at line becomes if salary >= (average - 5000) and salary <= (average + 5000)
Finally, you do not call averageSalaries before entering the loop, so the average salary haven't been computed yet. You should call the function and put the result in a variable before your for loop.

Python looping with try and except

I am trying to write a program that reads numbers input by the user until the user types done. If the user types a non-number other than "done," I want to return an error message like "please enter a number number. When the user types "done", I want to calculate the total of the numbers, the number count and the average. I have tried to create a while loop with try and except to catch the non-numeric error other than done. That is part of the trick, a string entry is an error unless the string is "done." Here is the beginning of my code without any attempt to create a file that can be totaled, counted and maxed.
bank = 0
number = 0
while True:
try:
number = int(raw_input("Enter an integer ( such as 49 or 3 or 16) \n"))
bank = bank + number
print 'You entered--- ', number, 'Your running total is ', bank
except:
if number == 'done':
print 'Done'
else:
if number == 'done':
print 'Done'
else:
print 'Your entry was non-numberic. Please enter a number.'
bank = number + bank
When I run this and enter "done" I get the "else:" response and a new input line. I do not get the "Done" print from if number == "done"
Answer written in python 3
The exception used is ValueError because the compiler catches this error as a result of the conversion done in line 7 so i just added the continue in line 19 to make it skip the error and go back to the start.
bank = 0
count = 0
while True:
try:
number = input('enter an integer:\n')
if number != 'done':
bank += int(number)
print('you entered -- ', number, 'your total is ', bank)
count += 1
elif number == 'done':
print('Done')
print('you entered %d numbers' % count)
print('Your total is %s' % bank)
average = bank/count
print('Your average is %.02f' % average)
break
except ValueError:
print('oops!! that was not an integer')
continue

Resources