Printing a kind of matrix in python - python-3.x

I have to print this Python-code:
Placement Name Won Played Percent Won
1 B Borg 5 10 0.5
2 J Smith 3 6 0.5
3
.
.
.
The number of players will vary, as the players are imported from a txt-file.
The top row (placement, name etc) is constant.
How can I use formatting to do this? Or if there is an easier way.
The info of the players should be aligned as is shown in the example code above.
EDIT:
def showLeaderboard(players):
print()
print("Placement, Name, Won, Played, Percent Won")
position = 1
for i in players:
print(position, end=" ")
i.showPlayerInfo()
position += 1
def readPlayers():
fileplacement = "leads to a directory"
if os.path.exists(fileplacement) and os.path.getsize(fileplacement) > 6:
txtinfo = open("players.txt", "r")
players = []
for row in txtinfo:
infoplayers = row.split(",")
players.append(Spelare(infoSpelare[0], float(infoSpelare[3]), int(infoSpelare[1]), int(infoSpelare[2]))) # "players" is a class, the rest is to import from my txt-file
return players
else:
print("players.txt does not seem to exist or contains unreasonables.")
raise SystemExit(0)

You need to iterate over your data two times. First, to see how wide each column must be, and next to actually print the data.
from typing import List
def prettyprint(headers: List[str], data: List[List[str]], separator: str=" ") -> None:
# initial column width is the width of the headers
columnwidths = [len(s) for s in headers]
for row in data:
# width of the columns in this row
cwidths = [len(s) for s in row]
# Take the max of the previously measured columnwidths and those of this row
columnwidths = [max(ow, cw) for ow, cw in zip(columnwidths, cwidths)]
prints = [headers] + data
for row in prints:
# Pad the data in each cell to the correct width
row_print = [cell.ljust(cwid) for cwid, cell in zip(columnwidths, row)]
print(separator.join(row_print))
d = [['1', 'B Borg', '5', '10', '0.5'], ['2', 'J Smith', '3', '6', '0.5']]
h = ['Placement', 'Name', 'Won', 'Played', 'Percent', 'Won']
prettyprint(h, d)
Gives the output:
Placement Name Won Played Percent
1 B Borg 5 10 0.5
2 J Smith 3 6 0.5
Obviously, you're going to have to modify this to work with your Spelare class. You can also use the functions rjust() and center() if you want a different alignment.

What you want to use is the following string methods:
ljust
rjust
center
Here is an example to get you started:
import sys
player_data = {
"Name": "B Borg",
"Placement": 1,
"Won": 5,
"Played": 10,
"Percent Won": 0.5
}
def print_format(k, val):
if k == "Name":
sys.stdout.write(val.ljust(20, ' '))
if k == "Placement":
sys.stdout.write(val.ljust(15, ' '))
if k == "Won":
sys.stdout.write(val.center(10, ' '))
if k == "Played":
sys.stdout.write(val.center(20, ' '))
if k == "Percent Won":
sys.stdout.write(val.center(10, ' '))
# Print the header
[print_format(k, k) for k in player_data.keys()]
print()
# Print the data:
[print_format(k, str(v)) for k, v in player_data.items()]
print()
(venv) [ttucker#zim bot]$ python justify.py
Name Placement Won Played Percent Won
B Borg 1 5 10 0.5

Related

Issues with 'movement' across multi-dimensional array, text-based game

I'm currently messing around with the code right now to solve some issues in movement. Ideally, i want to be able to move the character to any spot in the array(worldMap) containing ' ', and marking the player's position with 'P'.
I'm lost to what the issue I am running into is, and some of the issues I am running into are as follows...
- when I move E it always goes to worldMap[3][3]
- N only moves up from worldMap[3][x] to worldMap [2][x]
- S doesn't work
Below is my current code...
import math
import random
import sys
import os
import time
gameplay= True
#world map lay out, X--> wall, player can't go that way. ' ', possible area for player to move, 'P', player indicator.
worldMap = [['X','X','X','X','X'],
['X',' ',' ',' ','X'],
['X',' ',' ',' ','X'],
['X','P',' ',' ','X'],
['X','X','X','X','X']]
west=str("w" or "W")
east=str("e" or "E")
north=str('n' or 'N')
south=str('s' or 'S')
for row in worldMap:
for column in row:
print(column, end=' ')
print()
while gameplay == True:
x=str((input("\nWhat direction would you like to move?\n ")))
if x==west:
for row in worldMap:
for i, column in enumerate(row):
if column == 'P':
if((i>0) and (i<4) and row[i-1]) == ' ':
row[i-1] = 'P'
row[i]=' '
else:
print("\nCan't do that.\n")
for row in worldMap:
for column in row:
print(column, end=' ')
print()
elif x==east:
for row in worldMap:
for i, column in enumerate(row):
if column == 'P':
if((i>0) and (i<4) and row[i+1]) == ' ':
row[i+1] = 'P'
row[i]=' '
else:
print("\nCan't do that.\n")
for row in worldMap:
for column in row:
print(column, end=' ')
print()
elif x == north: #move north
for column in worldMap:
for i, row in enumerate(column):
if row == 'P':
if((i>0) and (i<4) and column[i-1]) == ' ':
column[i-1] = 'P'
column[i]=' '
else:
print("\nCan't do that.\n")
for row in worldMap:
for column in row:
print(column, end=' ')
print()
elif x== south: #move south
for column in worldMap:
for i, row in enumerate(column):
if column == 'P':
if((i>0) and (i<4) and column[i+1]) == ' ':
column[i+1] = 'P'
column[i]=' '
else:
print("\nCan't do that.\n")
for row in worldMap:
for column in row:
print(column, end=' ')
print()
else:
for row in worldMap:
for column in row:
print(column, end=' ')
print()
print("\nCan't do that.\n")
I have tided up your code a bit. A good clue to needing to refactor your code is if you have written the same thing alot in your code. In those cases its better to write a function and then call the function from your code. The below code is just an example to help set you on the right path.
In this example instead of scanning the whole map for the player, i just keep track of the players position and calculate the new position and if its a space then move the player. We can easily move the player by adding or subtracting 1 to the x or y co-ordinate of the player.
I also cleaned up the print map function and its now only called at the start of each loop.
from random import randint
def generate_map(size):
# world map lay out, X--> wall, player can't go that way. ' ', possible area for player to move, 'P', player indicator.
world_map = [[wall for _ in range(size)]]
for i in range(1, size - 1):
world_map.append([wall])
for j in range(1, size - 1):
#generate random walls in our map (currently set to genearete spaces 90% of the time
if randint(1,100) < 90:
world_map[i].append(space)
else:
world_map[i].append(wall)
world_map[i].append(wall)
world_map.append([wall for _ in range(size)])
return world_map
def print_map():
for row in worldMap:
print(" ".join(row))
def move_player():
x, y = player_position
if direction == "W":
y -= 1
elif direction == "E":
y += 1
elif direction == "N":
x -= 1
elif direction == "S":
x += 1
if worldMap[x][y] == space:
worldMap[player_position[0]][player_position[1]] = space
worldMap[x][y] = player
player_position[0] = x
player_position[1] = y
else:
print("Cannot more there")
#Genearte a map and position the player
wall, space, player = "X", " ", "P"
map_size = 10
worldMap = generate_map(map_size)
player_position = [1, 1]
worldMap[player_position[0]][player_position[1]] = player
#Run the game
gameplay = True
while gameplay:
print_map()
direction = input("\nWhat direction would you like to move? ").upper()
if direction:
move_player()
else:
break

How To Add 2 values in a dictionary together if the key is duplicated?

So I have this project where it asks the user for the number of .txt files these files are titled 'day' and then the number in ascending order. The format of what's inside the code is a sport/activity(key) then a comma and the number of people doing the sport(value). What I want is it to give an output of all the sports from the text files and if the activity(key) is duplicated then it adds up the people doing it(value). And to top that all off I wanted the total people who were participating(all the values added together)
days = int(input("How many days of records do you have? "))
i = 0
list1 = []
d = {}
for i in range(days):
i += 1
file = 'day' + str(i)
f = open(file + '.txt')
a = []
for line in f:
line = line.replace(',' , ' ')
list1.append(line)
words = line.split()
d[words[0]] = words[1]
a.append[words[1]]
stripped_line = [s.rstrip() for s in d]
for key,value in d.items() :
print (key + "," + value)
print("In total:", a, "attendees.")
INPUT
User_input = 3
day1.txt
swimming,1000
fencing,200
athletics,600
gymnastics,1200
tennis,500
day2.txt
diving,600
swimming,1200
tennis,500
rugby,900
handball,500
hockey,2300
trampoline,200
day3.txt
swimming,400
gymnastics,1200
fencing,100
diving,400
tennis,600
rugby,600
EXPECTED OUTPUT
swimming: 2600
fencing: 300
athletics: 600
gymnastics: 2400
tennis: 1600
diving: 1000
rugby: 1500
handball: 500
hockey: 2300
trampoline: 200
In total: 13000 attendees.
CURRENT OUTPUT
swimming,400
fencing,100
athletics,600
gymnastics,1200
tennis,600
diving,400
rugby,600
handball,500
hockey,2300
trampoline,200
This is one approach using collections.defaultdict.
Ex:
from collections import defaultdict
days = int(input("How many days of records do you have? "))
result = defaultdict(int)
for i in range(days):
with open("day{}.txt".format(i)) as infile:
for line in infile:
key, value = line.strip().split(",")
result[key] += int(value)
for key,value in result.items():
print (key + "," + str(value))
print("In total: {} attendees.".format(sum(result.values())))

Extending current code to include both median and mode

I have this line of code that i used for one assignment, but i can't figure out how to add the median and mode into the code to let it run without error.
def main():
filename = input('File name: ')
num=0
try:
infile = open(filename, 'r')
count = 0
total = 0.0
average = 0.0
maximum = 0
minimum = 0
range1 = 0
for line in infile:
num = int(line)
count = count + 1
total = total + num
if count == 1:
maximum = num
minimum = num
else:
if num > maximum:
maximum = num
if num < minimum:
minimum = num
if count > 0:
average = total / count
range1 = maximum - minimum
I'll jump right in and show you the code. It's a very simple and quite pythonic solution.
Solution
import statistics
def open_file(filename):
try:
return open(filename, 'r')
except OSError as e:
print(e)
return None
def main():
# Read file. Note that we are trusting the user input here without sanitizing.
fd = open_file(input('File name: '))
if fd is None: # Ensure we have a file descriptor
return
data = fd.read() # Read whole file
if data == '':
print("No data in file")
return
lines = data.split('\n') # Split the data into a list of strings
# We need to convert the list of strings to a list of integers
# I don't know a pythonic way of doing this.
for number, item in enumerate(lines):
lines[number] = int(item)
total_lines = len(lines)
total_sum = sum(lines)
maximum = max(lines)
minimum = min(lines)
# Here is the python magic, no need to reinvent the wheel!
mean = statistics.mean(lines) # mean == average
median = statistics.median(lines)
mode = "No mode!"
try:
mode = statistics.mode(lines)
except statistics.StatisticsError as ec:
pass # No mode, due to having the same quantity of 2 or more different values
print("Total lines: " + str(total_lines))
print("Sum: " + str(total_sum))
print("Max: " + str(maximum))
print("Min: " + str(minimum))
print("Mean: " + str(mean))
print("Median: " + str(median))
print("Mode: " + str(mode))
if __name__ == '__main__':
main()
Explanation
Generally, in python, it's safe to assume that if you want to calculate any mundane value using a well known algorithm, there will already be a function written for you to do just that. No need to reinvent the wheel!
These functions aren't usually hard to find online either. For instance, you can find suggestions regarding the statistics library by googling python calculate the median
Although you have the solution, I strongly advise looking through the source code of the statistics library (posted below), and working out how these functions work for yourself. It will help your grow as a developer and mathematician.
statistics.py
mean
def mean(data):
"""Return the sample arithmetic mean of data.
>>> mean([1, 2, 3, 4, 4])
2.8
>>> from fractions import Fraction as F
>>> mean([F(3, 7), F(1, 21), F(5, 3), F(1, 3)])
Fraction(13, 21)
>>> from decimal import Decimal as D
>>> mean([D("0.5"), D("0.75"), D("0.625"), D("0.375")])
Decimal('0.5625')
If ``data`` is empty, StatisticsError will be raised.
"""
if iter(data) is data:
data = list(data)
n = len(data)
if n < 1:
raise StatisticsError('mean requires at least one data point')
T, total, count = _sum(data)
assert count == n
return _convert(total/n, T)
median
def median(data):
"""Return the median (middle value) of numeric data.
When the number of data points is odd, return the middle data point.
When the number of data points is even, the median is interpolated by
taking the average of the two middle values:
>>> median([1, 3, 5])
3
>>> median([1, 3, 5, 7])
4.0
"""
data = sorted(data)
n = len(data)
if n == 0:
raise StatisticsError("no median for empty data")
if n%2 == 1:
return data[n//2]
else:
i = n//2
return (data[i - 1] + data[i])/2
mode
def mode(data):
"""Return the most common data point from discrete or nominal data.
``mode`` assumes discrete data, and returns a single value. This is the
standard treatment of the mode as commonly taught in schools:
>>> mode([1, 1, 2, 3, 3, 3, 3, 4])
3
This also works with nominal (non-numeric) data:
>>> mode(["red", "blue", "blue", "red", "green", "red", "red"])
'red'
If there is not exactly one most common value, ``mode`` will raise
StatisticsError.
"""
# Generate a table of sorted (value, frequency) pairs.
table = _counts(data)
if len(table) == 1:
return table[0][0]
elif table:
raise StatisticsError(
'no unique mode; found %d equally common values' % len(table)
)
else:
raise StatisticsError('no mode for empty data')

Sum of the items in a dictionary

I've been trying to do an exercise.. The objective is to sum the items and see which one has the highest value and return, each letter is corresponded in a value. For example, "Babel" worths 10 points (3+1+3+1+2) and "Xadrez" worths 21 points (8+1+2+1+1+8), so the program is supposed to return "Xadrez".
My code's this:
def better(l1):
dic = {'D':2, 'C':2, 'L':2, 'P':2, 'B':3, 'N':3, 'F':4, 'G':4,
'H':4, 'V':4, 'J':5, 'Q':6, 'X':8, 'Y':8, 'Z':8}
for word in dic.keys():
l1 = []
best = 0
sum = 0
word = word.split()
word = word.item()
sum = word.item()
best = word
l1 = l1.append(word)
return best
My idea is trying to split each word and sum the value of each letter in each word. Thanks.
Another example: (['ABACO', 'UTOPIA', 'ABADE']) >>'ABACO'
I would start by assigning a score value for every letter. This makes it easier to score the words because each letter has a specified value
points = \
{ 0: '_' # wild letter
, 1: 'eaionrtlsu'
, 2: 'dg'
, 3: 'bcmp'
, 4: 'fhvwy'
, 5: 'k'
, 8: 'jx'
, 10: 'qz'
}
You can easily build a dic like you have. It's better to define this outside of your scoring function because the dic can be reused instead of being recreated each time the function is run (as it does in your code)
dic = \
{ letter:score for (score,set) in points.items() for letter in set }
# { '_': 0
# , 'e': 1
# , 'a': 1
# , 'i': 1
# , ...
# , 'x': 8
# , 'q': 10
# , 'z': 10
# }
Scoring a word using dic is easy thanks to built-in sum function
def score_word (word):
return sum (dic[letter] for letter in word)
print (score_word ("babel")) # 9
print (score_word ("xadrez")) # 23
Now we need a max_word function that can determine the "max" of two words, w1 and w2
def max_word (w1, w2):
if (score_word (w1) > score_word (w2)):
return w1
else:
return w2
print (max_word ("babel", "xadrez")) # xadrez
Now it's easy to make a function that can accept any number of words, max_words
def max_words (w = None, *words):
if not words:
return w
else:
return max_word (w, max_words (*words))
print (max_words ('abaco', 'utopia', 'abade')) # abaco
print (max_words ()) # None

Python, using list of lists to update a self made board

I'm trying to put my list "Grid" into the board so whenever the user gives what column and row they want to fire at, it will update the guess onto the board. Any help on this?
def displayGrid(Rows,Columns):
output = ' |'
if (Rows >= 10) or (Columns >= 27):
print("Please pick a Row less than 10 or a number less than 27")
else:
for title in range(97,97+Columns):
output = output + chr(title)
output = output + ' |'
print(output.upper())
for row in range(Rows):
output = str(row+1) + '| '
for col in range(Columns):
output = output + ' | '
print(output)
displayGrid(Rows, Columns)
GuessRow = int(input("What row do you guess? \n"))
GuessColumn = int(input("What column do you guess? \n"))
def userGuess(GuessRow, GuessColumn):
grid = []
for row in range(Rows):
grid.append([])
for col in range(Columns):
grid[row].append('')
grid[GuessRow-1][GuessColumn-1] = 'X'
print(grid)
userGuess(GuessRow, GuessColumn)
Here are couple example functions. create_grid creates an empty grid of zeros. update_grid updates the specified index to an X. I find pprint helpful for nicely formatting nested tables. Also checkout tabulate library when you are working on output.
from pprint import pprint as pp
def create_grid(numRows,numColumns):
grid = []
for row in range(numRows):
row = []
for column in range(numColumns):
row.append(0)
grid.append(row)
return grid
def update_grid(grid, guessRow, guessColumn):
grid[guessColumn][guessRow] = 'X'
numRows = 7
numColumns = 7
grid = create_grid(numRows,numColumns)
pp(grid)
guessRow = 5
guessColumn = 2
update_grid(grid, guessRow, guessColumn)
pp(grid)

Resources