Creating a leaderboard for offline game in Python - python

For a school project, I'm creating a game that has a score system, and I would like to create some sort of leaderboard. Once finished, the teachers will upload it to a shared server where other students can download a copy of the game, but unfortunately students can't save to that server; if we could, leaderboards would be a piece of cake. There would at most be a few hundred scores to keep track of, and all the computers have access to the internet.
I don't know much about servers or hosting, and I don't know java, html, or any other language commonly used in web development, so other related questions don't really help. My game prints the scoring information to a text file, and from there I don't know how to get it somewhere online that everyone can access.
Is there a way to accomplish such a task with just python?
Here I have the code for updating a leaderboard file (assuming it would just be a text file) once I have the scores. This would assume that I had a copy of the leaderboard and the score file in the same place.
This is the format of my mock-leaderboard (Leaderboards.txt):
Leaderboards
1) JOE 10001
2) ANA 10000
3) JAK 8400
4) AAA 4000
5) ABC 3999
This is what the log-file would print - the initials and score (log.txt):
ABC
3999
Code (works for both python 2.7 and 3.3):
def extract_log_info(log_file = "log.txt"):
with open(log_file, 'r') as log_info:
new_name, new_score = [i.strip('\n') for i in log_info.readlines()[:2]]
new_score = int(new_score)
return new_name, new_score
def update_leaderboards(new_name, new_score, lb_file = "Leaderboards.txt"):
cur_index = None
with open(lb_file, 'r') as lb_info:
lb_lines = lb_info.readlines()
lb_lines_cp = list(lb_lines) # Make a copy for iterating over
for line in lb_lines_cp:
if 'Leaderboards' in line or line == '\n':
continue
# Now we're at the numbers
position, name, score = [ i for i in line.split() ]
if new_score > int(score):
cur_index = lb_lines.index(line)
cur_place = int(position.strip(')'))
break
# If you have reached the bottom of the leaderboard, and there
# are no scores lower than yours
if cur_index is None:
# last_place essentially gets the number of entries thus far
last_place = int(lb_lines[-1].split()[0].strip(')'))
entry = "{}) {}\t{}\n".format((last_place+1), new_name, new_score)
lb_lines.append(entry)
else: # You've found a score you've beaten
entry = "{}) {}\t{}\n".format(cur_place, new_name, new_score)
lb_lines.insert(cur_index, entry)
lb_lines_cp = list(lb_lines) # Make a copy for iterating over
for line in lb_lines_cp[cur_index+1:]:
position, entry_info = line.split(')', 1)
new_entry_info = str(int(position)+1) + ')' + entry_info
lb_lines[lb_lines.index(line)] = new_entry_info
with open(lb_file, 'w') as lb_file_o:
lb_file_o.writelines(lb_lines)
if __name__ == '__main__':
name, score = extract_log_info()
update_leaderboards(name, score)
Some more info:
The score would be less than 1 000 000
Ideally, the solution would just be some code external to the game, so that I would just make an executable that the user would run after they've finished
I know it doesn't sound very secure - and it isn't - but that's ok, it's doesn't need to be hackproof

The easiest is probably to just use MongoDB or something (MongoDB is a NoSQL type database that allows you to save dictionary data easily...)
You can use the free account at https://mongolab.com (that should give you plenty of space).
You will need pymongo as well pip install pymongo.
Then you can simply save records there:
from pymongo import MongoClient, DESCENDING
uri = "mongodb://test1:test1#ds051990.mongolab.com:51990/joran1"
my_db_cli = MongoClient(uri)
db = my_db_cli.joran1 # select the database ...
my_scores = db.scores # this will be created if it doesn't exist!
# add a new score
my_scores.insert({"user_name": "Leeeeroy Jenkins", "score": 124, "time": "11/24/2014 13:43:22"})
my_scores.insert({"user_name": "bob smith", "score": 88, "time": "11/24/2014 13:43:22"})
# get a list of high scores (from best to worst)
print(list(my_scores.find().sort("score", DESCENDING)))
Those credentials will actually work if you want to test the system (keep in mind I added leeroy a few times).

Related

Filling form fields in Word automatically with pywin32

I have a little problem to solve, but I donĀ“t get an acceptable solution. I try to have a script that automaticcaly fills in FormFields in Word with entries of dictionaries. So far I used pywin32 for that task and somehow I managed to find a solution that works at all. At least as long as I update the word document.
The command that I use so far in a loop is "marks(i).Result = keyValue". Here "marks" is - as far as I understand - a formfields object, which I get by "marks = doc.FormFields".
As I said, all this works to some extent, but as soon as I update the document in Word, all entries disappear again.
Would be really great if someone could help me and knows how to make it that the entry remains permanent. Unfortunately I do not understand the documentation of pywin32 at all. Attached below is the whole function.
Thanks in advance!
def wordExport(window, excDict, docPath):
wordApp = win32com.client.Dispatch('Word.Application') #Word App Object
wordApp.Visible = False
doc = wordApp.Documents.Open(docPath) #Open .docx
marks = doc.FormFields # FormFields object
cMarks = marks.Count # FormField count
cMatches = 0 #Match counter
cOverwrt = 0 #Fields overwritten
for i in range(1, cMarks + 1):
fieldName = str(marks(i).Name) #String object of FormField
#Default Cases --------------------------------------------------------------------
if fieldName in excDict:
keyValue = excDict.get(fieldName) #Get value to key(FormField)
resultIsEmpty = not marks(i).Result #No text has been entered before
# No prior entry present
if keyValue != "None"
marks(i).Result = keyValue + " "
cMatches += 1
if not resultIsEmpty:
cOverwrt += 1
doc.Close()
wordApp.Visible = False

Saving a list of class objects to a JSON file that can be later read

TLDR - I want to save a few variables to a JSON file. Some of these are lists of objects belonging to custom classes. An error is raised as JSON cannot serialize them. How can I do this?
I am doing a simple text-based top trumps program where the relevant classes are as follows:
Player (sub-classes below)
Human
AI
Card
I've only been coding for around 3 months. My code for the project itself is completely functional, but I'm implementing a load option on the opening and a save option at the end of each round.
Below is my config module, it contains all the variables that are accessed by other modules, and contains everything that'd be needed for a save/load.
import json
#All variables that need to be accessed between various modules publicly
total_players = 0
players = []
dead_players = []
num_of_humans = 0
num_of_ai = 0
total_cards = 0
cards = []
def save_file():
save_name = input("Save name (You will use this to load) > ")
path = 'path_to_dir{0}.json'.format(save_name)
data = {
'total_players' : total_players,
'players' : players,
'dead_players' : dead_players,
'num_of_humans' : num_of_humans,
'num_of_ai' : num_of_ai,
'total_cards' : total_cards,
'cards' : cards
}
with open(path, 'w+') as f:
json.dump(data, f)
def load_file():
load_name = input(f"Enter the name of your save > ")
path_two = 'path_to_dir{0}.json'.format(load_name)
with open(path_two, 'r') as f:
sf = json.load(f)
total_players = str(sf['total_players'])
players = str(sf['players'])
dead_players = str(sf['dead_players'])
num_of_humans = str(sf['num_of_humans'])
num_of_ai = str(sf['num_of_ai'])
total_cards = str(sf['total_card'])
cards = str(sf['cards'])
Description of these variables:
total_players is the total amount of players as an integer
players is a list of objects that belong to either the Human or Ai class
dead_players is same as above
(self explanatory below)
num_of_humans is int
num_of_ai is int
total cards is int
cards is a list of objects of class Card
My aim is to store the state of all of these variables and be able to load them accordingly to the variables at the top. In it's current state, JSON cannot serialize the objects of my custom classes.
In load_file(), I think it should actually be:
total_cards = str(sf['total_cards'])
change your load_file with this:
json.dump(data, f,default=lambda o:o.__dict__)
This should solve your problem.

TypeError: 'DictWriter' object is not iterable

I'm working on creating a short simple program for a nonprofit fundraiser to validate ticket numbers as guests check in to make sure no duplicate tickets are redeemed. I'm running Python 3.4.3 on a Windows 10 machine. Once the program is finalized it will be used on a Raspberry Pi with touchscreen at the fundraiser.
I've tried a couple different methods to build the list, save it, and search for duplicates. Ideally the list will be stored in a CSV file, but a plain text or other format is ok too.
Can you help me with the traceback error (TypeError: 'DictWriter' object is not iterable) due to the looping function to check ticket #'s against a list stored in a file to make sure no duplicate tickets are redeemed?
Thank you in advance for your help!
version = "v1.4"
fname="tickets.csv"
import csv
import datetime
import os.path
print("\nWelcome to TicketCheck", version)
extant = os.path.isfile(fname)
with open(fname, 'a', newline='') as csvfile:
fieldnames = ['ticketid', 'timestamp']
ticketwriter = csv.DictWriter(csvfile, fieldnames=fieldnames)
if extant == False:
ticketwriter.writeheader()
while True:
ticket = ""
print("Please enter a ticket # to continue or type exit to exit:")
ticket = str(input())
if ticket == "":
continue
if ticket == "exit":
break
print("You entered ticket # %s." % (ticket))
print("Validating ticket...")
for row in ticketwriter:
if row[0] == ticket:
print("\n\n\n===== ERROR!!! TICKET # %s ALREADY CHECKED IN =====\n\n\n" % (ticket))
continue
time = datetime.datetime.now()
print("Thank you for checking in ticket # %s at %s \n\n\n" % (ticket, time))
print("Ticket is now validated.")
ticketwriter.writerow({'ticketid': ticket, 'timestamp': time})
csvfile.flush()
continue
csvfile.close()
print("All your work has been saved in %s.\n Thank you for using TicketCheck %s \n" % (fname, version))
Hmm, I think you might be over-complicating this a bit! For something like that there's really no need to go to all that trouble. This is a great spot to use a dictionary, and for something with only two inputs, the id and the check-in time, you can easily just make a .txt log. I get the feeling this might be more of what you are looking for.
import time
go = True
while go:
the_guestlist = {}
the_ticket = input().strip()
file = open('somefile.txt', 'r')
for line in file:
my_items = line.split(',')
the_guestlist[my_items[0]] = my_items[1]
file.close()
if the_ticket in the_guestlist.keys():
print("Sorry, that ticket has been entered at {}".format(the_guestlist[the_ticket]))
elif the_ticket == 'exit':
go = False
print('Exiting...')
else:
the_guestlist[the_ticket] = '{}'.format(time.asctime())
file = open('somefile.txt', 'a')
file.write(the_ticket +','+the_guestlist[the_ticket]+'\n')
file.close()
Objects of the csv.DictWriter class are not iterable, i.e. you cannot iterate over them like you would a dictionary, list, or even string, hence your error message. It does not store the data you have previously written to file, only the file you wrote to stores that data.
To achieve your goal, you could do two things: either open your CSV file every time a new ticket needs to be validated, and check if the ticket number is present, or - since you are using relatively small amounts of data - store a dictionary in memory, and only write it out at the end of use, checking from that if the ticket is valid.

Trying to build an Organizational Tree From a List

I have an input file that's in the following format.
Fred,Karl,Technician,2010--Karl,Cathy,VP,2009--Cathy,NULL,CEO,2007--
--Vince,Cathy,Technician,2010
I need to parse this information to where it ends up looking something like this in an output file:
Cathy (CEO) 2007
-Karl (VP) 2009
--Fred (Technician) 2010
-Vince (Technician) 2010
With the CEO at the top, each subordinate should be under their superior. So whatever the second name is, that is the supervisor. The trick is that if an employee has 2 supervisors, they need to be indented twice "--" with their immediate supervisor above.
I've tried iterating through the list and parsing through the "--" and the commas but I'm struggling with the structure itself. This is what I have so far.
with open('org_chart_sample.in', 'r') as reader: # Open the input file
with open('output.out', 'w') as writer: # Make output file writable
reader.readline() # Ignore first line
lines = reader.readlines() # Read input lines
for line in lines: # Parse out input by the -- which separated attributes of people in the org
employees = line.split('--')
hierarchy = [] # Exterior list to aid in hierarchy
for employee in employees: # Logic that adds to the hierarchy list as algorithm runs
info = employee.split(',')
hierarchy.append(info)
I've been stuck on this problem for longer that I'd like to admit :(
Cool question, it was fun to work on. I tried to be thorough, and it ended up getting kind of long, I hope it's still readable.
Code:
##########################
#Input data cleaned a bit#
##########################
lines = ["Fred,Karl,Technician,2010",
"Karl,Cathy,VP,2009",
"Cathy,NULL,CEO,2007",
"Vince,Cathy,Technician,2010",
"Mary,NULL,CEO,2010",
"Steve,Mary,IT,2013"]
##################################
#Worker class to make things neat#
##################################
class Worker(object):
#Variables assigned to this worker
__slots__ = ["name","boss","job","year","employees","level"]
#Initialize this worker with a string in the form of:
#"name,boss,job,year"
def __init__(self,line):
self.name,self.boss,self.job,self.year = line.split(",")
self.level = 0 if self.boss == "NULL" else -1 #If boss is NULL, they are '0' level
self.employees = []
#A function to add another worker as this worker's employee
def employ(self,worker):
worker.level = self.level+1
self.employees.append(worker)
#This is a recursive function which returns a string of this worker
#and all of this workers employees (depth first)
def __repr__(self):
rep_str = ""
rep_str += "-"*self.level
rep_str += str(self.name)+" works for "+str(self.boss)
rep_str += " as a "+str(self.job)+" since "+str(self.year)+"\n"
for employee in self.employees:
rep_str += str(employee)
return rep_str
########################################
#Prepare to assign the bosses employees#
########################################
#1. Turn all of the lines into worker objects
workers = [Worker(line) for line in lines]
#2. Start from the top level bosses (the ones that had NULL as boss)
boss_level = 0
#3. Get a list of all the workers that have a boss_level of 0
bosses = [w for w in workers if w.level == boss_level]
#While there are still boses looking to employ then keep going
while len(bosses) > 0:
#For each boss look through all the workers and see if they work for this boss
#If they do, employ that worker to the boss
for boss in bosses:
for worker in workers:
if worker.level == -1 and boss.name == worker.boss:
boss.employ(worker)
#Move down a tier of management to sub-bosses
#If there are no sub-bosses at this level, then stop, otherwise while loop again
boss_level += 1
bosses = [w for w in workers if w.level == boss_level]
##########################
#Printing out the workers#
##########################
#1. Loop through the top bosses and
# print out them and all their workers
top_bosses = [w for w in workers if w.level == 0]
for top_boss in top_bosses:
print top_boss
Output:
Cathy works for NULL as a CEO since 2007
-Karl works for Cathy as a VP since 2009
--Fred works for Karl as a Technician since 2010
-Vince works for Cathy as a Technician since 2010
Mary works for NULL as a CEO since 2010
-Steve works for Mary as a IT since 2013

Trying to check if 2 values match in a file

this is a code from a chat bot, and it's purpose is to save into a file all information about an user. That will work fine as long as it's only in 1 room, but if i want to save information of the same user in 2 different rooms, i got a problem. The bot won't just update the information getting the user and the room, instead it will always create new and new lines of that user and that room.
It's getting annoying and i would really like to not break this code a lot, so i'd like to know where it fails and how to fix it in a proper way without using dicts. (You can read all the comments inside the code to understand how i think it works).
Thank you for your time.
#First of all it reads the file
leyendoestadisticas = open("listas\Estadisticas.txt", "r")
bufferestadisticas = leyendoestadisticas.read()
leyendoestadisticas.close()
if not '"'+user.name+'"' in bufferestadisticas: #If the name of the user is not there, it adds all the information.
escribiendoestadisticas = open("listas\Estadisticas.txt", 'a')
escribiendoestadisticas.write(json.dumps([user.name, palabrasdelafrase, letrasdelafrase,
"1", user.nameColor, user.fontColor, user.fontFace, user.fontSize,
message.body.replace('"', "'"), room.name, 0, "primermensajitodeesapersona", fixedrooms])+"\n")
escribiendoestadisticas.close()
else: #If the name it's there, it will do the next:
#First of all, get all rooms where the name is saved, to do that...
listadesalas = []
for line in open("listas\Estadisticas.txt", 'r'):
retrieved3 = json.loads(line)
if retrieved3[0] == user.name: #If the name is found
if not retrieved3[9] == room.name: #But room is diferent
listadesalas.append(retrieved3[9]) #Adds the room to a temporal list
#Now that we got a list with all different lines of that user based on rooms... we do the next code
data = []
hablaenunanuevasala = "no"
with open('listas\Estadisticas.txt', 'r+') as f:
for line in f:
data_line = json.loads(line)
if data_line[0] == user.name: #If name is there
if data_line[9] == room.name: #And the room matches with actual room, then update that line.
data_line[1] = int(data_line[1])+int(palabrasdelafrase)
data_line[2] = int(data_line[2])+int(letrasdelafrase)
data_line[3] = int(data_line[3])+1
data_line[4] = user.nameColor
data_line[5] = user.fontColor
data_line[6] = user.fontFace
data_line[7] = user.fontSize
data_line[11] = data_line[8]
data_line[8] = message.body.replace('"', "'")
data_line[9] = room.name
data_line[12] = fixedrooms
else: #but if the user is there and room NOT matches, we want to add a new line to the file with the same user but a new room.
if not room.name in listadesalas: #And here is where i believe is the problem of my code.
hablaenunanuevasala = "si" #needed since i didn't found a way to properly add a new line inside this loop, so must be done outside the loop later.
data.append(data_line)
f.seek(0)
f.writelines(["%s\n" % json.dumps(i) for i in data])
f.truncate()
#Outside the loop - This would work if the program noticed it's a room that is not saved yet in the file for that user.
if hablaenunanuevasala == "si":
escribiendoestadisticas2 = open("listas\Estadisticas.txt", 'a')
escribiendoestadisticas2.write(json.dumps([user.name, palabrasdelafrase, letrasdelafrase,
"1", user.nameColor, user.fontColor, user.fontFace, user.fontSize,
message.body.replace('"', "'"), room.name, 0, "primermensajitodeesapersona", fixedrooms])+"\n")
escribiendoestadisticas2.close()
So... that's what i tried, and it works perfect as long as it's 1 room, it updates the info all the time. When i speak in a second room, it adds me a new record with that second room (perfect). But then if i speak again in ANY of those 2 rooms, the bot will add 2 more lines of code to the file instead of updating the information of the room where i did speak.
Edit Let me summarize it:
Let's say I speak in "whenever" room, the file will save a record
["saelyth", "whenever", "more info"]
If i speak in another room, the file should save a record
["saelyth", "anotherroom", "more info"]
It works great... but then it doesn't update the info. If now i speak in any of those 2 rooms, instead of updating the proper line, the bot will add more new lines into the file, wich is the problem.
Fix done... somehow.
I did choose to save info into different files for each room, that works.

Categories

Resources