Bingo card generating duplicate numbers - python

I created a simple Bingo card generator that generates 4 numbers between n and n+15. However, I noticed that numbers are sometimes generated twice despite an if statement I wrote that was supposed to find duplicates and rerun the random generation for that cell. My code is below:
import random
ballCount = 75 # Max value of cell
gridWidth = 4
gridHeight = 5
numPerCard = 20 # Numbers generated per card
minimum = 1
maximum = 15
def generate_number(stored, small_num, big_num):
random.seed(a=None, version=2)
random_number = str(random.randint(small_num, big_num))
if "| "+random_number+" |" in stored:
generate_number(stored, small_num, big_num)
return random_number
for row in range(gridHeight):
string = ""
for Number in range(gridWidth):
number = generate_number(string, minimum, maximum)
string += " | " + number
string += " |"
print(string)
maximum += 15
minimum += 15
And here is an example of output with duplicates (23 appears 2 times):
| 2 | 5 | 9 | 12 |
| 25 | 23 | 19 | 23 |
| 37 | 42 | 30 | 37 |
| 60 | 49 | 50 | 55 |
| 73 | 71 | 69 | 67 |

if "| "+random_number+" |" in stored:
generate_number(stored, small_num, big_num)
return random_number
If you already have the number, you call generate_number again, but you still return the original number (the duplicate).
You may be interested in random.sample as an alternative way of getting a random selection of unique numbers within a range. E.g.:
>>> [random.sample(range(n, n+15), 4) for n in range(15, 75, 15)]
[[20, 26, 15, 23], [37, 44, 32, 40], [48, 45, 47, 50], [63, 62, 68, 71]]
or, with pretty-printing:
>>> for row in [random.sample(range(n, n+15), 4) for n in range(15, 75, 15)]:
... print(f"| {' | '.join(map(str, row))} |")
...
| 17 | 15 | 27 | 22 |
| 34 | 33 | 42 | 36 |
| 54 | 51 | 55 | 48 |
| 68 | 73 | 61 | 72 |

Inside the if statement you need to return the result from generate_number. Right now it will do the if statement and then discard that result and then return the original number.

Problems found in your code:
1) Your 'stored' variable in generate_number is missing the vertical bar "|" in the end, so number doesn't match.
2) The return of your recursive function must be assigned to "random_number".
Follow the corrected code:
import random
ballCount = 75 # Max value of cell
gridWidth = 4
gridHeight = 5
numPerCard = 20 # Numbers generated per card
cards = 6 # Number of cards
minimum = 1
maximum = 15
def generate_number(stored, small_num, big_num):
random.seed(a=None, version=2)
random_number = str(random.randint(small_num, big_num))
if "| " + random_number + " |" in stored +" |":
random_number = generate_number(stored, small_num, big_num)
return random_number
for row in range(gridHeight):
string = ""
for Number in range(gridWidth):
number = generate_number(string, minimum, maximum)
string += " | " + number
string += " |"
print(string)
maximum += 15
minimum += 15
Hope this helps

There are two problems I can see here. The first problem is, when your program retries the random number generation, it doesn't do anything with the result. You should add a return before generate_number(stored, small_num, big_num), making it return generate_number(stored, small_num, big_num). Secondly, you should remove the " |" in the "| "+random_number+" |". It can be possible that the repetition is at the last cell you generated, and therefore does not have a " |" after it. Hope this answers your question.

Related

Issue with pretty tables in python

I'm currently working on a assignment for school, Im not asking for anyone to solve the problem for me, Im just trying to figure out what it is im doing wrong with pretty tables. Here is the code I have for the table itself
import random
from prettytable import PrettyTable
x = PrettyTable
def generate_bingo_card():
dict = {'B': [], 'I': [], 'N': [], 'G': [], 'O': []}
counter = 0
while True:
if counter == 5:
break
else:
randb = random.randint(1, 15)
randi = random.randint(16, 30)
randn = random.randint(31, 45)
randg = random.randint(46, 60)
rando = random.randint(61, 75)
dict['B'].append(randb)
dict['I'].append(randi)
dict['N'].append(randn)
dict['G'].append(randg)
dict['O'].append(rando)
counter += 1
return dict
def print_bingo_card(card):
x.add_column('B', [card['B']])
x.add_column('I', [card['I']])
x.add_column('N', [card['N']])
x.add_column('G', [card['G']])
x.add_column('O', [card['O']])
print(print_bingo_card(generate_bingo_card()))
and here is the error im getting
File "C:\Users\Joshu\OneDrive\Desktop\Python assignment 3\a3q6_bingo.py", line 26, in print_bingo_card
x.add_column('B', [card['B']])
TypeError: PrettyTable.add_column() missing 1 required positional argument: 'column'
I've followed the documentation to a tee and am still getting this error, If someone could point me in the right direction that would be great!
I think this is a much more elegant solution; you have added complexity that I don't feel needs to exist; you can always retrofit this to your solution;
import prettytable
import random
def create_bingo_card():
card = {}
card['B'] = random.sample(range(1, 16), 5)
card['I'] = random.sample(range(16, 31), 5)
card['N'] = random.sample(range(31, 46), 5)
card['G'] = random.sample(range(46, 61), 5)
card['O'] = random.sample(range(61, 76), 5)
card['N'] = random.sample(range(76, 95), 5)
# create a pretty table
pt = prettytable.PrettyTable(field_names=['B', 'I', 'N', 'G', 'O'])
for row in zip(*[card['B'], card['I'], card['N'], card['G'], card['O']]):
pt.add_row(row)
return pt
card = create_bingo_card()
print(card)
Output:
+----+----+----+----+----+
| B | I | N | G | O |
+----+----+----+----+----+
| 1 | 17 | 37 | 54 | 74 |
| 5 | 21 | 33 | 57 | 64 |
| 6 | 24 | 78 | 60 | 71 |
| 10 | 19 | 44 | 47 | 69 |
| 2 | 30 | 41 | 52 | 62 |
+----+----+----+----+----+

How should I solve logic error in timestamp using Python?

I have written a code to calculate a, b, and c. They were initialized at 0.
This is my input file
-------------------------------------------------------------
| Line | Time | Command | Data |
-------------------------------------------------------------
| 1 | 0015 | ACTIVE | |
| 2 | 0030 | WRITING | |
| 3 | 0100 | WRITING_A | |
| 4 | 0115 | PRECHARGE | |
| 5 | 0120 | REFRESH | |
| 6 | 0150 | ACTIVE | |
| 7 | 0200 | WRITING | |
| 8 | 0314 | PRECHARGE | |
| 9 | 0318 | ACTIVE | |
| 10 | 0345 | WRITING_A | |
| 11 | 0430 | WRITING_A | |
| 12 | 0447 | WRITING | |
| 13 | 0503 | WRITING | |
and the timestamps and commands are used to process the calculation for a, b, and c.
import re
count = {}
timestamps = {}
with open ("page_stats.txt", "r") as f:
for line in f:
m = re.split(r"\s*\|\s*", line)
if len(m) > 3 and re.match(r"\d+", m[1]):
count[m[3]] = count[m[3]] + 1 if m[3] in count else 1
#print(m[2])
if m[3] in timestamps:
timestamps[m[3]].append(m[2])
#print(m[3], m[2])
else:
timestamps[m[3]] = [m[2]]
#print(m[3], m[2])
a = b = c = 0
for key in count:
print("%-10s: %2d, %s" % (key, count[key], timestamps[key]))
if timestamps["ACTIVE"] > timestamps["PRECHARGE"]: #line causing logic error
a = a + 1
print(a)
Before getting into the calculation, I assign the timestamps with respect to the commands. This is the output for this section.
ACTIVE : 3, ['0015', '0150', '0318']
WRITING : 4, ['0030', '0200', '0447', '0503']
WRITING_A : 3, ['0100', '0345', '0430']
PRECHARGE : 2, ['0115', '0314']
REFRESH : 1, ['0120']
To get a, the timestamps of ACTIVE must be greater than PRECHARGE and WRITING must be greater than ACTIVE. (Line 4, 6, 7 will contribute to the first a and Line 8, 9, and 12 contributes to the second a)
To get b, the timestamps of WRITING must be greater than ACTIVE. For the lines that contribute to a such as Line 4, 6, 7, 8, 9, and 12, they cannot be used to calculate b. So, Line 1 and 2 contribute to b.
To get c, the rest of the unused lines containing WRITING will contribute to c.
The expected output:
a = 2
b = 1
c = 1
However, in my code, when I print a, it displays 0, which shows the logic has some error. Any suggestion to amend my code to achieve the goal? I have tried for a few days and the problem is not solved yet.
I made a function that will return the commands in order that match a pattern with gaps allowed.
I also made a more compact version of your file reading.
There is probably a better version to divide the list into two parts, the problem was to only allow elements in that match the whole pattern. In this one I iterate over the elements twice.
import re
commands = list()
with open ("page_stats.txt", "r") as f:
for line in f:
m = re.split(r"\s*\|\s*", line)
if len(m) > 3 and re.match(r"\d+", m[1]):
_, line, time, command, data, _ = m
commands.append((line,time,command))
def search_pattern(pattern, iterable, key=None):
iter = 0
count = 0
length = len(pattern)
results = []
sentinel = object()
for elem in iterable:
original_elem = elem
if key is not None:
elem = key(elem)
if elem == pattern[iter]:
iter += 1
results.append((original_elem,sentinel))
if iter >= length:
iter = iter % length
count += length
else:
results.append((sentinel,original_elem))
matching = []
nonmatching = []
for res in results:
first,second = res
if count > 0:
if second is sentinel:
matching.append(first)
count -= 1
elif first is sentinel:
nonmatching.append(second)
else:
value = first if second is sentinel else second
nonmatching.append(value)
return matching, nonmatching
pattern_a = ['PRECHARGE','ACTIVE','WRITING']
pattern_b = ['ACTIVE','WRITING']
pattern_c = ['WRITING']
matching, nonmatching = search_pattern(pattern_a, commands, key=lambda t: t[2])
a = len(matching)//len(pattern_a)
matching, nonmatching = search_pattern(pattern_b, nonmatching, key=lambda t: t[2])
b = len(matching)//len(pattern_b)
matching, nonmatching = search_pattern(pattern_c, nonmatching, key=lambda t: t[2])
c = len(matching)//len(pattern_c)
print(f'{a=}')
print(f'{b=}')
print(f'{c=}')
Output:
a=2
b=1
c=1

Updating a row with multiple different values in python with sqlite3

I have looked at many similar sources to this question but can't seem to find an answer that solves my problem.
I am using python3.7.1 and sqlite3 to essentially create quizzes. I have an array of questions which I have inserted into a table, with a 'Class_name' and 'quizNumber' using this code example:
quizQuestion = ['1 + 1 =', '2 + 2 =', '3 + 3 =',...]
quizNumber = (1)
quizClass = ('6D')
for question in quizQuestions:
cursor.execute( "INSERT INTO set_questionsTbl (QuizNumber, Class_name, Question) VALUES (?,?,?)", (quizNumber,quizClass,question))
This code works fine and the table(set_questionsTbl) looks like this:
QuizNumber | Class_name | Question | Answer
-------------------------------------------
1 | 6D | 1 + 1 = |
1 | 6D | 2 + 2 = |
1 | 6D | 3 + 3 = |
etc..
I also have an array of answers:
quizAnswers = [2,4,6,...]
The problem that occurs is when trying to update the table with the answers so it looks like this:
QuizNumber | Class_name | Question | Answer
-------------------------------------------
1 | 6D | 1 + 1 = | 2
1 | 6D | 2 + 2 = | 4
1 | 6D | 3 + 3 = | 6
etc...
The code I tried was this:
for answer in quizAnswers:
cursor.execute("UPDATE set_questionsTbl SET Answer = (?) ", (answer,))
This didn't work as with every loop the previous inputted answer got overwritten leaving me with this:
QuizNumber | Class_name | Question | Answer
-------------------------------------------
1 | 6D | 1 + 1 = | 6
1 | 6D | 2 + 2 = | 6
1 | 6D | 3 + 3 = | 6
etc...
I have also tried joining the loops together but that doesn't work, table looks like:
QuizNumber | Class_name | Question | Answer
-------------------------------------------
1 | 6D | 1 + 1 = | 2
1 | 6D | 2 + 2 = | 2
1 | 6D | 3 + 3 = | 2
1 | 6D | 1 + 1 = | 4
1 | 6D | 2 + 2 = | 4
1 | 6D | 3 + 3 = | 4
1 | 6D | 1 + 1 = | 6
1 | 6D | 2 + 2 = | 6
1 | 6D | 3 + 3 = | 6
I have tried to correct this many times and searched many different examples but couldn't seem to find a solution. So how do I loop through each question and update the answer with each answer in quizAnswers?
I am new to stack overflow so I may have missed a question similar to this, if so please link it.
The point you are missing is, that you instruct your database to update all records with the answer in the current iteration of the loop.Your example:
quizAnswers = [2,4,6]
for answer in quizAnswers:
cursor.execute("UPDATE set_questionsTbl SET Answer = (?) ", (answer,))
basically does:
set Answer for *all* records to 2
set Answer for *all* records to 4
set Answer for *all* records to 6
Your database does not understand the meaning of the data you put into the tables. For the DB, the questions you inserted into the Question column of your set_questionsTbl are just sequences of characters. It has no means of evaluating the equation in Question in order to set a value in Answer only if that value is the result of the equation.
You need to tell your DB which answer to set for which question(s).
You need to give your query criteria, that a record must meet for the update operation to be applied to it. That's what WHERE clauses are for.
Following your example, you'll want to create a correlation between questions and their respective answers. The below is one way to do that.
First, create a list of tuples. Each tuple contains an answer in the first element, the second element holds the corresponding question (this needs to be the exact value you inserted into the Question column).
quiz_question_answers = [(2, '1 + 1 ='), (4, '2 + 2 ='), (6, '3 + 3 =')]
Then you can iterate over that list of tuples to execute the UPDATE query in your cursor:
for q_a in quiz_question_answers:
cursor.execute("UPDATE set_questionsTbl SET Answer = ? WHERE Question = ?", (q_a[0], q_a[1]))
This will update the answer in all records that have the specific equation in Question. Records with different Class_name and/or QuizNumber - a record like this for example:
QuizNumber | Class_name | Question | Answer
-------------------------------------------
4 | 5A | 1 + 1 = |
- will be updated as well, because the only criteria, the Question equaling 1 + 1 =, is met for this record also.
If you want the answers to be set only for questions of quiz number 1 for class 6D, you'll have to add more restrictive criteria to your query, e.g.:
quiz_question_answers = [(2, '1 + 1 =', 1, '6D'), (4, '2 + 2 =', 1, '6D'), (6, '3 + 3 =', 1, '6D')]
for q_a in quiz_question_answers:
cursor.execute("UPDATE set_questionsTbl "
"SET Answer = ? "
"WHERE Question = ? "
"AND QuizNumber = ? "
"AND Class_name = ?",
(q_a[0], q_a[1], q_a[2], q_a[3]))
With this particular solution (using a list of tuples with the query parameters in the correct order) you can also use executemany to let the cursor do the loop over the list of parameter tuples for you:
quiz_question_answers = [(2, '1 + 1 ='), (4, '2 + 2 ='), (6, '3 + 3 =')]
cursor.executemany("UPDATE set_questionsTbl SET Answer = ? WHERE Question = ?", quiz_question_answers)
Here's some further reading on WHERE clauses specifically for SQLite.
You should have a unique ID for at least one of the fields in your table. That way when you execute the answer update, you could add a WHERE clause, e.g:
cursor.execute( "UPDATE set_questionsTbl SET answer=? WHERE unique_id=?", (answer,uniqueId))
and that way not all of your records will be updated.

Python - Huffman bit compression

I need help on this. I made a Huffman compression program. For example I'll input "google", I can just print it like this:
Character | Frequency | Huffman Code
------------------------------------
'g' | 2 | 0
'o' | 2 | 11
'l' | 1 | 101
'e' | 1 | 100
I also need to print the combined bits so if I use that it will be:
011101100
Which is wrong because as far as I understand Huffman compression it should be:
011110101100
So my output should be:
Character | Frequency | Huffman Code
------------------------------------
'g' | 2 | 0
'o' | 2 | 11
'o' | 2 | 11
'g' | 2 | 0
'l' | 1 | 101
'e' | 1 | 100
Basically I need to display it based on what I input. So if I input "test" it should also print test vertically with their corresponding bits since I'm just appending the bits and displaying them. How do I achieve this? Here's the printing part:
freq = {}
for c in word:
if c in freq:
freq[c] += 1
else:
freq[c] = 1
freq = sorted(freq.items(), key=lambda x: x[1], reverse=True)
if check:
print (" Char | Freq ")
for key, c in freq:
print (" %4r | %d" % (key, c))
nodes = freq
while len(nodes) > 1:
key1, c1 = nodes[-1]
key2, c2 = nodes[-2]
nodes = nodes[:-2]
node = NodeTree(key1, key2)
nodes.append((node, c1 + c2))
nodes = sorted(nodes, key=lambda x: x[1], reverse=True)
if check:
print ("left: %s" % nodes[0][0].nodes()[0])
print ("right: %s" % nodes[0][0].nodes()[1])
huffmanCode = huffman(nodes[0][0])
print ("\n")
print (" Character | Frequency | Huffman code ")
print ("---------------------------------------")
for char, frequency in freq:
print (" %-9r | %10d | %12s" % (char, frequency, huffmanCode[char]))
P.S. I know I shouldn't be sorting them I'll remove the sorting part don't worry

How do I put data from a while loop into a table?

Basically I'm estimating pi using polygons. I have a loop which gives me a value for n, ann and bnn before running the loop again. here is what I have so far:
def printPiTable(an,bn,n,k):
"""Prints out a table for values n,2n,...,(2^k)n"""
u = (2**k)*n
power = 0
t = ((2**power)*n)
while t<=u:
if power < 1:
print(t,an,bn)
power = power + 1
t = ((2**power)*n)
else:
afrac = (1/2)*((1/an)+(1/bn))
ann = 1/afrac
bnn = sqrt(ann*bn)
print(t,ann,bnn)
an = ann
bn = bnn
power = power + 1
t = ((2**power)*n)
return
This is what I get if I run it with these values:
>>> printPiTable(4,2*sqrt(2),4,5)
4 4 2.8284271247461903
8 3.3137084989847607 3.0614674589207187
16 3.1825978780745285 3.121445152258053
32 3.1517249074292564 3.1365484905459398
64 3.1441183852459047 3.1403311569547534
128 3.1422236299424577 3.1412772509327733
I want to find a way to make it instead of printing out these values, just print the values in a nice neat table, any help?
Use string formatting. For example,
print('{:<4}{:>20f}{:>20f}'.format(t,ann,bnn))
produces
4 4.000000 2.828427
8 3.313708 3.061467
16 3.182598 3.121445
32 3.151725 3.136548
64 3.144118 3.140331
128 3.142224 3.141277
{:<4} is replaced by t, left-justified, formatted to a string of length 4.
{:>20f} is replaced by ann, right-justified, formatted as a float to a string of length 20.
The full story on the format string syntax is explained here.
To add column headers, just add a print statement like
print('{:<4}{:>20}{:>20}'.format('t','a','b'))
For fancier ascii tables, consider using a package like prettytable:
import prettytable
def printPiTable(an,bn,n,k):
"""Prints out a table for values n,2n,...,(2^k)n"""
table = prettytable.PrettyTable(['t', 'a', 'b'])
u = (2**k)*n
power = 0
t = ((2**power)*n)
while t<=u:
if power < 1:
table.add_row((t,an,bn))
power = power + 1
t = ((2**power)*n)
else:
afrac = (1/2)*((1/an)+(1/bn))
ann = 1/afrac
bnn = sqrt(ann*bn)
table.add_row((t,ann,bnn))
an = ann
bn = bnn
power = power + 1
t = ((2**power)*n)
print(table)
printPiTable(4,2*sqrt(2),4,5)
yields
+-----+---------------+---------------+
| t | a | b |
+-----+---------------+---------------+
| 4 | 4 | 2.82842712475 |
| 8 | 3.31370849898 | 3.06146745892 |
| 16 | 3.18259787807 | 3.12144515226 |
| 32 | 3.15172490743 | 3.13654849055 |
| 64 | 3.14411838525 | 3.14033115695 |
| 128 | 3.14222362994 | 3.14127725093 |
+-----+---------------+---------------+
Perhaps it is overkill for this sole purpose, but Pandas can make nice tables too, and can export them in other formats, such as HTML.
You can use output formatting to make it look pretty. Look here for an example:
http://docs.python.org/release/1.4/tut/node45.html

Categories