Simple word scrabble code using python that takes command line parameters - python

I am trying to create a simple word scrabble script that finds words score values. The script should reads two parameters from the command line and display the best word value, which returns the word with highest point value. I have created a constructor that reads the file and populate a dictionary of letter/value for use with the remaining methods of the class. For example the command line parameters should look like the following:
scrabble.py c:\tiles.txt apple,squash
Output: The best value is squash with 18.
Here is what I have so far. I know that import argv is helful, but not sure how to get started.
from sys import argv
class Scrabble:
def __init__(self, tilesFile):
with open(tilesFile, "r") as f:
lines = [ line.strip().split(":") for line in f ]
self.tiles = { k:int(v) for k,v in lines }
def getWordValue(self, word):
sum = 0
for letter in word.upper():
sum += self.tiles[letter]
return sum
def getBestWord(self):
pass
def main():
s1 = Scrabble("tile_values.txt")
value = s1.getWordValue("hello")
print(value)
if __name__ == '__main__':
main()
pass

What you need is to use the argparse module
https://docs.python.org/3/library/argparse.html
I took your example and added argparse. There are some issues with your Scrabble constructor. But you will get the idea of using args on the command line
python scrabble.py tiles.txt apple squash orange
import argparse
import sys
class Scrabble:
def __init__(self, tilesFile):
with open(tilesFile, "r") as f:
lines = [ line.strip().split(":") for line in f ]
self.tiles = { k:int(v) for k,v in lines }
def getWordValue(self, word):
sum = 0
for letter in word.upper():
sum += self.tiles[letter]
return sum
def getBestWord(self):
pass
def main(argv):
s1 = Scrabble(argv[1])
if len(argv) < 3:
print 'no words given'
sys.exit(1)
# argv[2:] means the items in argv starting from the 3rd item.
# the first item will be the name of the script you are running
# the second item should be the tiles file.
for word in argv[2:]:
value = s1.getWordValue(word)
print(value)
if __name__ == '__main__':
main(argv)

You can get the arguments you script was passed on the command line with sys.argv.
from sys import argv
print("This is a list of all of the command line arguments: ", argv)

Related

Python unittest a module instead of a function

I'm trying to write a test.py file to test a module I wrote. The specifications of the program are that I take serial user input and then print, not return a single answer. The first line of user input indicates how many inputs will follow. With an example program, "4\n1\n2\n3\n4\n" would mean there are 4 inputs and the inputs are [1,2,3,4]. Here is an example of the program that would take the input (sumEx.py):
import sys
def sum():
n = int(sys.stdin.readline().strip())
nums = []
for _ in range(n):
nums.append(int(sys.stdin.readline().strip()))
result = 0
for num in nums:
result += num
print(result)
if __name__ == "__main__":
sum()
I realize that in this example the for loop is redundant, but this is just an example for the actual program I am working on to abstract the problem. Currently, this is the test file I have:
from io import StringIO
import sys
from _pytest.monkeypatch import MonkeyPatch
import unittest
from sumEx import sum as program
class Testing(unittest.TestCase):
def test_string(self):
monkeypatch = MonkeyPatch()
monkeypatch.setattr('sys.stdin', StringIO("8\n1\n2\n3\n4\n5\n6\n7\n8\n"))
self.assertEqual(program(), 36)
def test_boolean(self):
monkeypatch = MonkeyPatch()
monkeypatch.setattr('sys.stdin', StringIO("4\0\n1\n2\n3\n"))
self.assertEqual(program(), 6)
if __name__ == '__main__':
unittest.main()
The problem is that my tests will only work if I returned them instead of printing them. Ideally, my test file would call the file sumEx.py and then
if __name__ == "__main__":
sum()
would call the sum function, the test would supply the input (like an actual person typing each line), and then whatever sum prints would be considered the output for the test. Any help is greatly appreciated. Please ask any questions if something is too vague. Thank you!
If anyone is curious, this is what I'm gonna go with for now. This takes input from a file and mimics user input through sys.stdin. It then reads the correct output from a file and compares it to the output of the program. It also runs the same test case with different inputs with the help of parameterization. Thank you #MrBeanBremen for the suggestion!
class Testing(unittest.TestCase):
def load_file(self, fileName, inOut):
try:
inputFile = open(fileName, 'r')
fileInput = r'{}'.format(inputFile.readline())
for line in inputFile.readlines():
fileInput = r'{}'.format(fileInput + line)
if inOut == 'in':
fileInput = r'{}'.format(fileInput+'\n')
inputFile.close()
return fileInput
except:
print("ERROR LOADING FILE")
#parameterized.expand([
["./tests/test0in.txt", "./tests/test0out.txt"],
["./tests/test1in.txt", "./tests/test1out.txt"],
["./tests/test2in.txt", "./tests/test2out.txt"]])
#patch('sys.stdout', new_callable=StringIO)
def test(self, inputFile, outputFile, mock_stdout):
monkeypatch = MonkeyPatch()
monkeypatch.setattr('sys.stdin', StringIO(self.load_file(inputFile, "in")))
program.main()
self.assertEqual(mock_stdout.getvalue(), self.load_file(outputFile, "out"))
if __name__ == '__main__':
unittest.main(verbosity=2)

How to write a function that takes in the name of a file as the argument in Python?

I need to create a function return_exactly_one(file_name) that takes in the name of a text file as the argument, opens the text file, and returns a list that only contains words that occurred exactly once in the text file. My file is test.txt, but I have trouble about the argument of the function. I'm not allowed to take test.txt as the argument, because it's an invalid variable. And when I call the function, what should I put into the parenthesis? How to solve it? Thanks. My code is as follows.
import string
def return_exactly_one(test):
test = open("test.txt", "r")
text = test.read()
test.close()
for e in string.punctuation:
if e in text:
text = text.replace(e, "")
text_list = text.split()
word_count_dict = {}
for word in text_list:
if word in word_count_dict:
word_count_dict[word] +=1
else:
word_count_dict[word] = 1
once_list = []
for key, val in word_count_dict.items():
if val == 1:
once_list.append(key)
return once_list
print(__name__)
if __name__ == "__main__":
print("A list that only contains items that occurred exactly once in the text file is:\n{}.".format(return_exactly_one(test)))
Your function should take a string filename as a parameter, like this:
def return_exactly_one(filename):
test = open(filename, "r")
...
and then you would call the function like:
return_exactly_one("test.txt")
I'm not sure what's preventing you from doing that. You need only store the filename as a string and pass the string to the function. So for example, you could take the filename as input like so:
file_name = input("Enter the name of the file:")
And then call the function with the file name like so:
return_exactly_one(file_name)
Also, inside the function you'd open it this way:
test = open(file_name, "r")
# Notice, no quotes, it's a variable here, not a string

Create a function generateString(char, val) that returns a string with val number of char characters concatenated together

I have been working this for days and I can only come up with:
def generateString(char, val):
Unless I'm missing something, this should work if char is a string and val is an integer.
def generateString(char, val):
return char * val
i did this
import sys
import random
character= sys.argv[1]
count= int(sys.argv[2])
# Your code goes here
def generateString(char, count):
random = char * count
return random
print(character*count)
Program Output
Input: s 3
Your Output: sss
If you just print(character * count) of course you will get the right output but the goal is to create an operational function and then output by using the function
import sys
character= sys.argv[1]
count= int(sys.argv[2])
def generateString(char, val):
string = char * val
return string
print(generateString(character, count))
I was able to get this code to work:
# Get our arguments from the command line
import sys
character= sys.argv[1]
count= int(sys.argv[2])
# Your code goes here
def generateString(char, val):
return char * val
print (generateString('s',3))
Without just printing the answer, this is what worked for me:
# Get our arguments from the command line
import sys
character= sys.argv[1]
count= int(sys.argv[2])
# Your code goes here
def generateString(char, val):
str = print(char * val)
return str
generateString(character, count)

'MarkovGenerator' object has no attribute 'together'

I come up with a problem about the class and I don't know the reason, does anyone can help me out?
The problem is in def together(), here are my code.
class MarkovGenerator(object):
def __init__(self, n, max):
self.n = n # order (length) of ngrams
self.max = max # maximum number of elements to generate
self.ngrams = dict() # ngrams as keys; next elements as values
beginning = tuple(["That", "is"]) # beginning ngram of every line
beginning2 = tuple(["on", "the"])
self.beginnings = list()
self.beginnings.append(beginning)
self.beginnings.append(beginning2)
self.sentences = list()
def tokenize(self, text):
return text.split(" ")
def feed(self, text):
tokens = self.tokenize(text)
# discard this line if it's too short
if len(tokens) < self.n:
return
# store the first ngram of this line
#beginning = tuple(tokens[:self.n])
#self.beginnings.append(beginning)
for i in range(len(tokens) - self.n):
gram = tuple(tokens[i:i+self.n])
next = tokens[i+self.n] # get the element after the gram
# if we've already seen this ngram, append; otherwise, set the
# value for this key as a new list
if gram in self.ngrams:
self.ngrams[gram].append(next)
else:
self.ngrams[gram] = [next]
# called from generate() to join together generated elements
def concatenate(self, source):
return " ".join(source)
# generate a text from the information in self.ngrams
def generate(self,i):
from random import choice
# get a random line beginning; convert to a list.
#current = choice(self.beginnings)
current = self.beginnings[i]
output = list(current)
for i in range(self.max):
if current in self.ngrams:
possible_next = self.ngrams[current]
next = choice(possible_next)
output.append(next)
# get the last N entries of the output; we'll use this to look up
# an ngram in the next iteration of the loop
current = tuple(output[-self.n:])
else:
break
output_str = self.concatenate(output)
return output_str
def together(self):
return "lalala"
if __name__ == '__main__':
import sys
import random
generator = MarkovGenerator(n=2, max=16)
for line in open("us"):
line = line.strip()
generator.feed(line)
for i in range(2):
print generator.generate(i)
print generator.together()
But I got the error saying:
Traceback (most recent call last):
File "markovoo2.py", line 112, in <module>
print generator.together()
AttributeError: 'MarkovGenerator' object has no attribute 'together'
Does anyone know know the reason?
You have indented the def together() function definition too far, it is part of the def generate() function body.
Un-indent it to match the other functions in the class body.
It looks your def together is indented too deeply. It is inside the generate method. Move it out one indentation level.

loop fails when increasing string from elements in list

I have some problem with sequence generator. I have a file where each line contain one fragment (8 letters). I load it from file in to list, where each element is one fragment. It is DNA so it should go that way:
1. Takes first 8-letter element
2. Check for element in which first 7 letters is the same as last 7 letters in first.
3. Add 8th letter from second element in to sequence.
It should look like this:
ATTGCCAT
TTGCCATA
TGCAATAC
So sequence: ATTGCCATAC
Unfortunately it only add one element. :( First element is given (we knew it). I do it that way its first in file (first line).
Here is the code:
from os import sys
import random
def frag_get(seqfile):
frags = []
f_in = open(seqfile, "r")
for i in f_in.readlines():
frags.append(i.strip())
f_in.close()
return frags
def frag_list_shuffle(frags):
random.shuffle(frags)
return frags
def seq_build(first, frags):
seq = first
for f in frags:
if seq[-7:] == f[:7]:
seq += f[-1:]
return seq
def errors():
pass
if __name__ == "__main__":
frags = frag_get(sys.argv[1])
first = frags[0]
frags.remove(first)
frags = frag_list_shuffle(frags)
seq = seq_build(first, frags)
check(sys.argv[2], seq)
spectrum(sys.argv[2], sys.argv[3])
I have deleted check and spectrum functions because it's simple calculations e.g. length comparison, so it is not what cause a problem as I think.
I will be very thankfully for help!
Regards,
Mateusz
Because your fragments are shuffled, your algorithm needs to take that into account; currently, you're just looping through the fragments once, which is unlikely to include more than a few fragments if they're not in the right order. For example, say you have 5 fragments, which I'm going to refer to by their order in your sequence. Now the fragments are slightly out of order:
1 - 3 - 2 - 4 - 5
Your algorithm will start with 1, skip 3, then match on 2, adding a base at the end. Then it'll check against 4 and 5, and then finish, never reaching fragment 3.
You could easily fix this by starting your loop again each time you add a base, however, this will scale very badly for a large number of bases. Instead, I'd recommend loading your fragments into a trie, and then searching the trie for the next fragment each time you add a base, until you've added one base for each fragment or you can no longer find a matching fragment.
works for me:
>>> seq = "ATTGCCAT"
>>> frags = ["TTGCCATA", "TGCCATAC"]
>>> for f in frags:
... if seq[-7:] == f[:7]:
... seq += f[-1:]
...
>>> seq
'ATTGCCATAC'
You have a spelling error in your example, TGCAATAC should be TGCCATAC. But fixing that it works.
For fun and interest, I've rewritten the problem using OO. See what you think:
import collections
import sys
import random
usage = """
Usage:
sequence fname expected
Where
fname: name of file containing fragments
expected: result-string which should be obtained by chaining from first fragment.
"""
class Frag(str):
MATCHLEN = 7
def __new__(cls, s=''):
return str.__new__(cls, s.strip())
def head(self):
return Frag(self[:Frag.MATCHLEN])
def tail(self):
return Frag(self[Frag.MATCHLEN:])
def nexthead(self):
return Frag(self[-Frag.MATCHLEN:])
def check(self, s):
return self.__eq__(s)
def __add__(self, s):
return Frag(str(self).__add__(s))
class Fraglist(list):
#classmethod
def fromFile(cls, fname):
with open(fname, "r") as inf:
lst = [Frag(ln) for ln in inf]
return cls(lst)
def shuffle(self):
random.shuffle(self)
class Sequencer(object):
def __init__(self, seq=None):
super(Sequencer, self).__init__()
self.sequences = collections.defaultdict(list)
if seq is not None:
for frag in seq:
self.sequences[frag.head()].append(frag.tail())
def build(self, frag):
res = [frag]
match = frag.nexthead()
while match in self.sequences:
next = random.choice(self.sequences[match])
res.append(next)
match = (match + next).nexthead()
return Frag(''.join(res))
def main():
if len(sys.argv) != 3:
print usage
sys.exit(-1)
else:
fname = sys.argv[1]
expected = sys.argv[2]
frags = Fraglist.fromFile(fname)
frag1 = frags.pop(0)
frags.shuffle()
seq = Sequencer(frags)
result = seq.build(frag1)
if result.check(expected):
print "Match!"
else:
print "No match"
if __name__=="__main__":
main()

Categories

Resources