Sheepulator in python - Neowise - 15-07-2023
Removing this out of game sign-ups as its not the right place to be updating on this.
I'll update here instead.
I've gotten most of the user interface working now.
I think i'll work on getting some outputs working next.
My next post will explain how you can see this working in a browser, no need to install python or modules to follow along if you want to see progress, and also how somebody on a chromebook could use this code.
Code: from tkinter import *
from tkinter import filedialog, messagebox, ttk
import xml.etree.ElementTree as ET
from enum import Enum
import re
class EdPlayer:
def __init__(self,*args):
if len(args)==0:
self.Name = ""
self.Answers = []
self.OriginalPosition = NewPlayerOriginalPosition
self.StartScore = 0
self.NeedsRegrouping = FALSE
elif len(args) == 1:
newname=args[0]
newAnswers=[]
self.Name = newname
self.Answers = newAnswers
self.StartScore = 0
self.NeedsRegroupIng=FALSE
self.OriginalPosition = -1
elif len(args) == 2:
newname=args[0]
newAnswers=args[1]
self.Name = newname
self.Answers = newAnswers
self.StartScore = 0
self.NeedsRegroupIng=FALSE
self.OriginalPosition = -1
elif len(args) == 3:
newname=args[0]
newAnswers=args[1]
startscore=args[2]
self.Name = newname
self.Answers = newAnswers
self.StartScore = startscore
self.NeedsRegroupIng = FALSE
self.OriginalPosition = -1
elif len(args) == 4:
newname=args[0]
newAnswers=args[1]
startscore=args[2]
origpos=args[3]
self.Name = newname
self.Answers = newAnswers
self.StartScore = startscore
self.NeedsRegroupIng = FALSE
self.OriginalPosition = origpos
class ShGame:
class ShQuestion:
def __init__(self, ref_game, new_text):
self.Text = new_text
self.Groups= []
self._Game = ref_game
def __str__(self):
return f'Question: {self.Text} Groups: {self.Groups} ref_game: {self._Game}'
@property
def GameIndex(self):
return self._Game.Questions.index(self)
@property
def Game(self):
return self._Game
def StartNewGroup(self, new_text):
newGrp = ShGame.ShGroup(self, new_text)
self.Groups.append(newGrp)
return newGrp
def SyncGroups(self):
self.Groups = [shg for shg in self.Groups if shg.Answers]
def GetAllAnswers(self):
all_answers = []
for grp in self.Groups:
all_answers.extend(grp.Answers)
return all_answers
# returns list of scores for this question
# SCORING METHODS
# Sheep: each player gets total answers in his group as his score
# Peehs1: incorrects = 1.5 * highest correct score
# Peehs2: incorrects = highest correct score + 0.5 * number of distinct
# correct answers
# Heep: highest score gets 0, 2nd highest get doubled
# Kangaroo: must be incorrect; correct answers get 0
def Scores(include_bonus):
curScore = {}
for plr in _Game.Players:
curScore[plr] = 0
if len(_Game.Players) == 0 or len(_Game.Questions) == 0:
return curScore
highest_score = 0
second_highest_score = 0
num_distinct_correct = 0
for grp in self.Groups:
for ans in grp.Answers:
if ans.Player not in curScore:
continue
curScore[ans.Player] = len(grp.Answers)
return curScore
# for peehs/heep
if grp.Correct:
num_distinct_correct += 1
if len(grp.Answers) > highest_score:
second_highest_score = highest_score
highest_score = len(grp.Answers)
elif len(grp.Answers) > second_highest_score and len(grp.Answers) < highest_score:
second_highest_score = len(grp.Answers)
# apply special scores depending on scoring method
for grp in self.Groups:
for ans in grp.Answers:
if ans.Player not in curScore:
continue
if self._Game.Method == ShMethod.Sheep:
if not grp.Correct:
curScore[ans.Player] = 0
# incorrect means invalid for sheep
elif self._Game.Method == ShMethod.PeehsDM:
# incorrect -> 1.5*sheep
if not grp.Correct:
curScore[ans.Player] = 1.5 * highest_score
elif self._Game.Method == ShMethod.PeehsFB:
# incorrect -> sheep + 0.5*distinct
if not grp.Correct:
curScore[ans.Player] = highest_score + 0.5 * num_distinct_correct
elif self._Game.Method == ShMethod.PeehsHybrid:
# incorrect -> sheep + 0.5*distinct
if not grp.Correct:
curScore[ans.Player] = 1.25 * highest_score + 0.25 * num_distinct_correct
elif _Game.Method == ShMethod.Heep or _Game.Method == ShMethod.Heep15 or _Game.Method == ShMethod.Heep2:
if curScore[ans.Player] == highest_score or not grp.Correct:
curScore[ans.Player] = 0
elif curScore[ans.Player] == second_highest_score:
if _Game.Method == ShMethod.Heep15:
curScore[ans.Player] *= 1.5
elif _Game.Method == ShMethod.Heep2:
curScore[ans.Player] *= 2
elif _Game.Method == ShMethod.Kangaroo:
if grp.Correct:
curScore[ans.Player] = 0
elif _Game.Method == ShMethod.Manual:
curScore[ans.Player] = 0
# apply rounding
if _Game.Rounding == ShRoundingType.Up:
curScore[ans.Player] = math.ceil(curScore[ans.Player])
elif _Game.Rounding == ShRoundingType.Down:
curScore[ans.Player] = math.floor(curScore[ans.Player])
elif _Game.Rounding == ShRoundingType.Nearest:
curScore[ans.Player] = round(curScore[ans.Player])
# apply player & group bonuses
if include_bonus:
temp_score = cur_score[ans.Player]
if grp.BonusType == ShBonusType.Override:
temp_score = grp.GroupBonus
elif grp.BonusType == ShBonusType.Add:
temp_score += grp.GroupBonus
if ans.BonusType == ShBonusType.Override:
temp_score = ans.AnswerBonus
elif ans.BonusType == ShBonusType.Add:
temp_score += ans.AnswerBonus
cur_score[ans.Player] = temp_score
def score_up_to(include_bonus):
cur_score = {}
next_score = {}
for plr in _Game.Players:
cur_score[plr] = 0
for que in _Game.Questions:
if que.GameIndex <= self.GameIndex:
next_score = que.scores(include_bonus)
for k, v in next_score.items():
if k in cur_score:
cur_score[k] += v
return cur_score
class ShPlayer:
counter = 0
def __init__(self, ref_game, player_name, start_score=0):
self._Game = ref_game
self.Name = player_name
self.Answers = []
self.StartScore = start_score
ShGame.ShPlayer.counter += 1
def __str__(self):
return f'Player: {self.Name} Answers: {self.Answers} '
@property
def Count(self):
return self.counter
@property
def GameIndex(self):
return self._Game.Players.index(self)
@property
def Game(self):
return self._Game
def __del__(self):
for ans in self.Answers:
self.counter -= 1
ans.Group.Answers.remove(ans)
class ShGroup:
def __init__(self, ref_question, new_text):
self.Text = new_text
self.Correct = True
self.GroupBonus = 0
self.BonusType = NONE
self.Answers = []
self._Question = ref_question # reference to question
# constructor
# declares with an empty list for Answers
#self.Answers = [ShAnswer() for _ in range(len(ref_question.Game.Players))]
#self.Answers = [ShGame.ShAnswer(len(ref_question.Game.Players)) for _ in range(len(ref_question.Game.Players))]
def __str__(self):
return f'Group.Text: {self.Text} ref_question: {self._Question} Answers: {self.Answers}'
@property
def Question(self):
return self._Question
# moves all answers to ref_group and deletes itself
def MergeToGroup(self, ref_group):
if ref_group == self:
# throw new Exception("Trying to merge a group to itself")
return
while len(self.Answers) != 0:
self.Answers[0].ChangeGroup(ref_group)
self._Question.Groups.remove(self)
def GetScore(self, include_bonus):
if len(self.Answers) == 0:
return 0
else:
try:
baseScore = _Question.Scores(False)[self.Answers[0].Player]
if include_bonus:
if self.BonusType == ShBonusType.Override:
return self.GroupBonus
elif self.BonusType == ShBonusType.Add:
return self.GroupBonus + baseScore
else:
return baseScore
else:
return baseScore
except:
return 0
def __del__(self):
for ans in self.Answers:
ans.Player.Answers.remove(ans)
class ShAnswer:
def __init__(self, ref_group, ref_player, new_text):
self.Text = new_text
self.AnswerBonus = 0
self.BonusType = None
self._Group = ref_group
self._Player = ref_player
def __str__(self):
return f'Answer.Text:{self.Text} ref_group: {self._Group} ref_player: {self._Player}'
@property
def Group(self):
return self._Group
@property
def Player(self):
return self._Player
def ChangeGroup(self, ref_group):
#print(str(ref_group) +" "+str(self._Group))
if ref_group == self._Group:
return
if self._Group.Question != ref_group.Question:
raise Exception("Moving an answer to a group in a different question.")
oldGroup = self._Group
self._Group = ref_group
ref_group.Answers.append(self)
try:
oldGroup.Answers.remove(self)
except:
pass
if len(oldGroup.Answers) == 0:
oldGroup.Question.Groups.remove(oldGroup)
def StartNewGroup(self):
oldGroup = self._Group
#print(oldGroup)
newGroup = ShGame.ShGroup(self._Group.Question, self.Text)
#print(newGroup)
oldGroup.Question.Groups.append(newGroup)
self._Group = newGroup
newGroup.Answers.append(self)
oldGroup.Answers.remove(self)
if len(oldGroup.Answers) == 0:
oldGroup.Question.Groups.remove(oldGroup)
class ShMethod(Enum):
Sheep = 1
PeehsDM = 2
PeehsFB = 3
PeehsHybrid = 4
Heep = 5
Heep15 = 6
Heep2 = 7
Kangaroo = 8
Manual = 9
class ShBonusType(Enum):
NONE = 0
Add = 1
Override = 2
class ShRoundingType(Enum):
NONE = 0
Up = 1
Down = 2
Nearest = 3
def __init__(self,*args):
if len(args)==0:
self.Questions = []
self.Players = []
self.Method = self.ShMethod.Sheep
self.Rounding = self.ShRoundingType.NONE
elif len(args)==3:
new_questions=args[0]
new_players=args[1]
new_answers=args[2]
if len(new_answers) != len(new_questions) or len(new_answers[0]) != len(new_players):
raise Exception("Answer list must be size [num questions, num players]")
self.Questions = [ShQuestion(self, txt) for txt in new_questions]
self.Players = [ShPlayer(self, txt) for txt in new_players]
for iques in range(len(new_questions)):
for iplayer in range(len(new_players)):
new_group = ShGroup(self.Questions[iques], new_answers[iques][iplayer])
self.Questions[iques].Groups.append(new_group)
new_answer = ShAnswer(new_group, self.Players[iplayer], new_answers[iques][iplayer])
new_group.Answers.append(new_answer)
self.Players[iplayer].Answers.append(new_answer)
def __str__(self):
return 'ShGame'
# attempts to guess groupings for ans
def guess_group(self,ans):
#red
anstxt = re.sub(r'\W', '', ans.Text.lower())
for grp in ans.Group.Question.Groups:
#print(ans.Group.Question.Groups)
if grp == ans.Group:
continue
if re.sub(r'\W', '', grp.Text.lower()) == anstxt:
ans.ChangeGroup(grp)
return
# didn't find any but let's try individual answers
for grp in ans.Group.Question.Groups:
if grp == ans.Group:
continue
for ans2 in grp.Answers:
if re.sub(r'\W', '', ans2.Text.lower()) == anstxt:
ans.ChangeGroup(grp)
return
def loadReveal(self):
resetProgram()
global myLabel2, Roundtype
#filename = "C:/Users/keith/Desktop/sheep/test.sheep17"
filename = filedialog.askopenfilename(title="Load Sheep Scoring File", filetypes=[("Sheep Score 2017 File", "*.sheep17")])
tree = ET.parse(filename)
root = tree.getroot()
if root.tag == "SheepScore2012Game":
list=[]
for child in root: #sanity check file need uniquie player names
if child.tag == "Player":
list.append(child.text)
if len(list)!=len(set(list)):
messagebox.showinfo(title="File Load error", message="Duplicate name found in player list. All players require a unique player name, please fix the game file manually.")
return -1
for child in root:
if child.tag == "ScoringMethod":
self.Method = self.ShMethod[child.text]
#Gametype.set(gt[child.text])
elif child.tag == "Rounding":
if child.text =="None":
child.text = "NONE"
self.Rounding = self.ShRoundingType[child.text]
#Roundtype.set(rt[child.text])
elif child.tag == "Question":
#print(child.attrib['GameIndex'], child.text)
qindex = int(child.attrib['GameIndex'])
while len(self.Questions) < qindex + 1:
self.Questions.append(self.ShQuestion(self, "(blank)"))
self.Questions[qindex].Text = child.text
elif child.tag == "Player":
pindex = int(child.attrib['GameIndex'])
start_score = child.attrib['StartScore']
while len(self.Players) < pindex + 1:
self.Players.append(self.ShPlayer(self, "(blank)", start_score))
self.Players[pindex].Name = child.text.strip()
elif child.tag == "Group":
group_q_index = int(child.attrib['QuestionIndex'])
tempcorrect = child.attrib['Correct']
tempgroupbonus = child.attrib['GroupBonus']
tempgroupbonustype = child.attrib['BonusType']
newGroup = self.ShGroup(self.Questions[group_q_index], "")
newGroup.Correct = tempcorrect
newGroup.GroupBonus = tempgroupbonus
newGroup.BonusType = tempgroupbonustype
self.Questions[group_q_index].Groups.append(newGroup)
for item in child:
if item.tag == "Text":
newGroup.Text = item.text
elif item.tag == "Answer":
ans_p_index = int(item.attrib['PlayerIndex'])
tempansbonus = int(item.attrib["AnswerBonus"])
tempansbonustype = item.attrib['BonusType']
anstext = item.text
newAns = ShGame.ShAnswer(newGroup, self.Players[ans_p_index], anstext)
newAns.AnswerBonus = tempansbonus
newAns.BonusType = tempansbonustype
newGroup.Answers.append(newAns)
#print(f"newgroup {self.Questions[group_q_index].Groups}")
#self.Players[ans_p_index].Answers.append(newAns)
if len(self.Questions) >= curQ:
myLabel2.grid_forget()
myLabel2 = Label(window, text=self.Questions[curQ - 1].Text)
myLabel2.grid(row=0, column=5)
else:
qdown()
updateTreeview()
else:
messagebox.showinfo(title="File load error", message="This is not a recognized sheep file!")
def saveReveal(self):
filename = filedialog.asksaveasfilename(title="Save Sheep Scoring File", filetypes=[("Sheep Score 2017 File", "*.sheep17")])
sheep_score_game = ET.Element('SheepScore2012Game')
scoring_method = ET.SubElement(sheep_score_game, "ScoringMethod")
scoring_method.text = str(self.Method.name)
rounding = ET.SubElement(sheep_score_game, "Rounding")
rounding.text = str(self.Rounding.name)
for que in self.Questions:
question = ET.SubElement(sheep_score_game, "Question")
question.set("GameIndex", str(que.GameIndex))
question.text = que.Text
for plr in self.Players:
player = ET.SubElement(sheep_score_game, "Player")
player.set("GameIndex", str(plr.GameIndex))
player.set("StartScore", str(plr.StartScore))
player.text = plr.Name
for que in self.Questions:
for grp in que.Groups:
group = ET.SubElement(sheep_score_game, "Group")
group.set("QuestionIndex", str(que.GameIndex))
group.set("GroupBonus", str(grp.GroupBonus))
group.set("BonusType", str(grp.BonusType))
group.set("Correct", str(grp.Correct))
group2 = ET.SubElement(group , "Text")
group2.text = grp.Text
for ans in grp.Answers:
answer = ET.SubElement(group, "Answer")
answer.set("AnswerBonus", str(ans.AnswerBonus))
answer.set("BonusType", str(ans.BonusType))
answer.set("PlayerIndex", str(ans.Player.GameIndex))
answer.text = ans.Text
tree = ET.ElementTree(sheep_score_game)
ET.indent(tree, ' ')
tree.write(filename, encoding ='utf-8', xml_declaration = True)
def qdown():
global curQ
global myLabel2
if curQ>len(sg.Questions): curQ = len(sg.Questions)
if curQ>1:
curQ=curQ-1
myTextbox1.delete(0, END)
myTextbox1.insert(INSERT, curQ)
myTextbox1.grid_forget()
myTextbox1.grid(row=0, column=2)
if curQ == 0: return
if len(sg.Questions) >= curQ:
myLabel2.grid_forget()
myLabel2 = Label(window, text=sg.Questions[curQ-1].Text)
myLabel2.grid(row=0, column=5)
updateTreeview()
def qup():
global curQ
global myLabel2
if curQ > len(sg.Questions): curQ = len(sg.Questions)
if curQ < len(sg.Questions):
curQ = curQ + 1
else:
curQ == 1
myTextbox1.delete(0,END)
myTextbox1.insert(INSERT, curQ)
myTextbox1.grid_forget()
myTextbox1.grid(row=0, column=2)
if curQ == 0: return
if len(sg.Questions) >= curQ:
myLabel2.grid_forget()
myLabel2 = Label(window, text=sg.Questions[curQ-1].Text)
myLabel2.grid(row=0, column=5)
updateTreeview()
def resetProgram():
global myTextbox1, myLabel2, players, curQ, current_var, curP
sg.Questions = []
sg.Players = []
sg.Groups= []
players=[]
curQ = 1
curP = 0
current_var.set("")
myTextbox1.delete(0, END)
myTextbox1.insert(INSERT, curQ)
myTextbox1.grid_forget()
myTextbox1.grid(row=0, column=2)
myLabel2.grid_forget()
myCheckbox1.grid(row=0, column=4)
myLabel2 = Label(window, text="Click Sheep > Edit Questions... to begin.")
myLabel2.grid(row=0, column=5)
updateTreeview()
return
def edAL(edAText,combo):
global players, curP
players=[]
existing_players = []
answers = []
for x in players: existing_players.append(x.Name)
file1 = open(filedialog.askopenfilename(title="Load Players and Answers from File", filetypes=[("txt files", "*.txt")] ), 'r', encoding="utf8")
found = 0
next_player = 0
name = ""
for line in file1:
line=line.strip()
if found==0 and line[0:6]=="From: ":
found=1
name=line[6:]
if found==2 and line=="------------------------------------------------------------------------":
found = 0
next_player=1
if found==2:
answers.append(line)
if found==1 and line=="------------------------------------------------------------------------":
found = 2
if next_player==1:
if name not in existing_players:
player=EdPlayer(name)
existing_players.append(name)
index = 0 # question 0
for ans in answers:
player.Answers.append(ans)
answers = []
players.append(player)
next_player = 0
else:
print(name + "exists in "+str(existing_players))
print("Duplicate Player " + name)
answers = []
print("dumped duplicate")
next_player=0
players = sorted(players, key=lambda w: w.Name.lower())
combo['values'] = [item.Name for item in players]
combo.current(curP)
PAnswers = []
answers = ""
for x in players[curP].Answers: PAnswers.append(x)
for item in PAnswers: answers = answers + item + "\n"
edAText.delete(1.0, END)
edAText.insert(INSERT, answers)
edAText.grid(column=0, columnspan=3, rowspan=10, padx=5, pady=5)
return(edAText)
def edQL(edQText):
#file1 = open("C:/Users/keith/Desktop/sheep/questions.txt", 'r')
file1 = open(filedialog.askopenfilename(title="Load Questions from File", filetypes=[("txt files", "*.txt")] ), 'r')
strvar=file1.read()
edQText.delete(1.0,END)
edQText.insert(INSERT, strvar)
return(edQText)
def edSave(edQW,edQText):
global myLabel2, curQ
questions = edQText.get("1.0",END).splitlines()
for index, item in enumerate(questions):
while index == len(sg.Questions):
sg.Questions.append(ShGame.ShQuestion(sg,item))
sg.Questions[index].Text = item
while sg.Questions[len(sg.Questions)-1].Text.strip()=="":
sg.Questions.pop(len(sg.Questions)-1)
print("question popped")
window.deiconify()
if curQ==0:curQ=1
if curQ>len(sg.Questions): curQ=len(sg.Questions)
myTextbox1.delete(0, END)
myTextbox1.insert(INSERT, curQ)
myTextbox1.grid_forget()
myTextbox1.grid(row=0, column=2)
if len(sg.Questions) >= curQ:
myLabel2.grid_forget()
myLabel2 = Label(window, text=sg.Questions[curQ - 1].Text)
myLabel2.grid(row=0, column=5)
edQW.destroy()
def edPSave(edAW,edAText,combo):
global players, score, myPlayers, curP
if len(players)==0:
sg.Players = []
for x in range(len(sg.Questions)):
sg.Questions[x].Groups = []
if len(players)>0:
combo.event_generate('<<ComboboxSelected>>') #store values of currently selected player
who=players[curP].Name
players = sorted(players, key=lambda w: w.Name.lower())
sg.Players = sorted(sg.Players, key=lambda w: w.Name.lower())
index=0
for x in players:
if x.Name == who:
break
index += 1
curP = index
dbplayernames=[] # sg name
if len(sg.Players)>0:
for player in sg.Players:
dbplayernames.append(player.Name)
playernames=[] #ed names
for player in players: playernames.append(player.Name)
todeleteplayers=list(set(dbplayernames)-set(playernames))
newplayers=[]
for player in players: #add the name missing from playernames
if player.Name not in dbplayernames:
sg.Players.append(ShGame.ShPlayer(sg, player.Name, 0))
newplayers.append(player.Name)
for z in todeleteplayers: #Lets delete deleted players.
for x, player in enumerate(sg.Players):
if player.Name in todeleteplayers:
sg.Players.pop(x)
for x, player in enumerate(players):
if player.Name in todeleteplayers:
players.pop(x)
for ply in todeleteplayers:
for q, Qs in enumerate(sg.Questions):
for g, Gs in enumerate(Qs.Groups):
for a, As in enumerate(Gs.Answers):
if As.Player.Name == ply:
sg.Questions[q].Groups[g].Answers.pop(a)
if len(sg.Questions[q].Groups[g].Answers) == 0:
sg.Questions[q].Groups.pop(g)
#lists should be the same
players = sorted(players, key=lambda w: w.Name.lower())
sg.Players = sorted(sg.Players, key=lambda w: w.Name.lower())
dbplayernames = []
playernames = []
for player in sg.Players: dbplayernames.append(player.Name)
for player in players: playernames.append(player.Name)
for pnum, player in enumerate(players):
for qnum, anstxt in enumerate(player.Answers):
while qnum == len(sg.Questions):
sg.Questions.append(ShGame.ShQuestion(sg,""))
if player.Name in newplayers:
newgroup = ShGame.ShGroup(sg.Questions[qnum], anstxt)
sg.Players[pnum].Answers.append(ShGame.ShAnswer(newgroup, sg.Players[pnum], anstxt))
for g, grp in enumerate(sg.Questions[qnum].Groups):
if anstxt.lower == grp.Text.lower():
for a, ans in enumerate(grp.Answers):
if ans.Player.Name == player.Name:
if ans.Text.lower != anstxt.lower:
sg.Questions[qnum].Group[g].Answers.pop(ans) #del ans
if len(grp.Answers) ==0:
sg.Questions[qnum].Group.pop(g) #del grp
newgroup = ShGame.ShGroup(sg.Questions[qnum], anstxt)
sg.Players[pnum].Answers.append(ShGame.ShAnswer(newgroup, sg.Players[pnum], anstxt))
#Moving Answers from Players into groups.
for x, player in enumerate(sg.Players):
for i, ans in enumerate(player.Answers):
present_groups=[]
for m in ans.Group.Question.Groups:
present_groups.append(m.Text.lower())
if ans.Text.lower() not in present_groups:
ans.Group.Question.Groups.append(ShGame.ShGroup(ans.Group.Question,ans.Text))
present_groups.append(ans.Text.lower())
ans.Group.Question.Groups[present_groups.index(ans.Text.lower())].Answers.append(ans)
for x, player in enumerate(sg.Players):
for i, ans in enumerate(player.Answers):
sg.Players[x].Answers.pop(i)
updateTreeview()
window.deiconify()
edAW.destroy()
def edPCancel(parent,child):
parent.deiconify()
child.destroy()
def edCancel(child):
window.deiconify()
child.destroy()
def edPOK(edAText,combo, edAW, newPW, newplayer, x=''):
global players, score, curP
if newplayer!="":
names=[]
for x in players: names.append(x.Name)
if newplayer not in names and newplayer !='':
players.append(EdPlayer(newplayer.strip(),[],0,-1))
players = sorted(players,key=lambda w: w.Name.lower())
index=0
for x in players:
if x.Name==newplayer:
break
index+=1
curP=index
combo['values'] = [item.Name for item in players]
if curP > len(players) -1: curP=len(players)
combo.current(curP)
PAnswers = []
answers = ""
for x in players[curP].Answers: PAnswers.append(x)
for item in PAnswers: answers = answers + item + "\n"
edAText.delete(1.0, END)
edAText.insert(INSERT, answers)
edAText.grid(column=0, columnspan=3, rowspan=10, padx=5, pady=5)
edAW.deiconify()
newPW.destroy()
def edROK(edAText,combo, edAW, newPW, newplayer, x=''):
global players, score, curP
names=[]
for x in players: names.append(x.Name)
if newplayer not in names and newplayer !='':
players[curP].Name=newplayer.strip()
combo['values'] = [item.Name for item in players]
combo.current(curP)
edAW.deiconify()
newPW.destroy()
def newPlayer(edAText,combo, edAW):
TextBoxUpdate(edAText,combo)
edAW.withdraw()
newPW = Toplevel(edAW)
newPW.bind('<Return>', lambda x: edPOK(edAText,combo, edAW, newPW, newPEntry.get()))
newPW.title("New Player")
newPLabel = Label(newPW, text="Enter new player name:", padx=10, font=20).grid(row=0, column=0)
newPEntry = Entry(newPW, font=20)
newPEntry.grid(row=1, column=0)
newPOK = Button(newPW, text='OK', command=lambda: edPOK(edAText,combo, edAW, newPW, newPEntry.get())).grid(row=2,column=3)
newPCancel = Button(newPW, text='Cancel', command=lambda: edPCancel(edAW, newPW)).grid(row=2,column=4)
newPEntry.focus_force()
def renamePlayer(edAText, combo, edAW):
global players
if len(players) > 0:
TextBoxUpdate(edAText, combo)
edAW.withdraw()
newPW = Toplevel(edAW)
newPW.bind('<Return>', lambda x: edROK(edAText, combo, edAW, newPW, newPEntry.get()))
newPW.title("Rename Player")
newPLabel = Label(newPW, text="Rename the player:", padx=10, font=20).grid(row=0, column=0)
entry_text=StringVar()
newPEntry = Entry(newPW, font=20, textvariable=entry_text)
entry_text.set(combo.get())
newPEntry.grid(row=1, column=0)
newPOK = Button(newPW, text='OK', command=lambda: edROK(edAText, combo, edAW, newPW, newPEntry.get())).grid(row=2, column=3)
newPCancel = Button(newPW, text='Cancel', command=lambda: edPCancel(edAW, newPW)).grid(row=2, column=4)
newPEntry.focus_force()
def delPlayer(edAText,combo):
global players, curP
if len(players)>0:
names=[]
for x in players: names.append(x.Name)
index=names.index(combo.get())
players.pop(index)
if curP==len(players):
curP-=1
combo['values'] = [item.Name for item in players]
if len(players)==0:
combo.set('')
PAnswers = []
answers = ""
edAText.delete(1.0, END)
edAText.grid(column=0, columnspan=3, rowspan=10, padx=5, pady=5)
else:
combo.current(curP)
PAnswers = []
answers = ""
for x in players[curP].Answers: PAnswers.append(x)
for item in PAnswers: answers = answers + item + "\n"
edAText.delete(1.0, END)
edAText.insert(INSERT, answers)
edAText.grid(column=0, columnspan=3, rowspan=10, padx=5, pady=5)
combo.event_generate('<<ComboboxSelected>>')
def TextBoxUpdate(edAText,combo):
global curP, current_var, players
#Get current answers
if len(players)>0:
answers=edAText.get(1.0, END).splitlines()
if answers[-1].lstrip()=='':
del answers[-1]
index=0
if len(answers) >= len (players[curP].Answers):
while index < len(players[curP].Answers):
if answers[index]!=players[curP].Answers[index]:
players[curP].Answers[index]=answers[index]
index+=1
while index < len(answers):
players[curP].Answers.append(answers[index])
index+=1
if len(answers) < len(players[curP].Answers):
while index < len(answers):
if answers[index]!=players[curP].Answers[index]:
players[curP].Answers[index]=answers[index]
index+=1
while index < len(players[curP].Answers):
del players[curP].Answers[index]
current_value = current_var.get()
index=0
for item in players:
if item.Name==current_value:
curP=index
else: index+=1
if len(players) != 0:
combo.current(curP)
PAnswers=[]
answers = ""
for x in players[curP].Answers: PAnswers.append(x)
for item in PAnswers: answers=answers+str(item)+"\n"
edAText.delete(1.0, END)
edAText.insert(INSERT, answers)
edAText.grid(column=0, columnspan=3, rowspan=10, padx=5, pady=5)
def edAnswers(window):
global players, curP, current_var
def select_next(event):
selection = combo.current() # get the current selection
last = len(combo['values']) - 1 # index of last item
key = event.keysym # get the key that was pressed
if key == 'Up':
try:
combo.current(selection - 1) # set the combobox to the previous item
combo.event_generate('<<ComboboxSelected>>')
except TclError: # end of list reached
pass #combo.current(last) # wrap around to last item
elif key == 'Down':
try:
combo.current(selection + 1) # set the combobox to the next item
combo.event_generate('<<ComboboxSelected>>')
except TclError: # end of list reached
pass #combo.current(0) # wrap around to first item
return 'break' # tell tk to dispose of this event and don't show the menu!
window.withdraw()
edAW = Toplevel(window)
edAW.bind("<<ComboboxSelected>>", lambda x: TextBoxUpdate(edAText,combo))
if len(players)==0:
players=[]
if len(sg.Players) != 0:
playerlist=[]
for x in players: playerlist.append(x.Name)
for qnum, question in enumerate(sg.Questions):
for gnum, grp in enumerate(sg.Questions[qnum].Groups):
for a, ans in enumerate(grp.Answers):
if ans.Player.Name not in playerlist:
players.append(EdPlayer(ans.Player.Name))
playerlist.append(ans.Player.Name)
if (len(players[playerlist.index(ans.Player.Name)].Answers)) == qnum:
players[playerlist.index(ans.Player.Name)].Answers.append(ans.Text)
if curP >= len(players):
curP = len(players)-1
answers=""
if len(players)==0:
answers='Click Load... to load players and answers\nfrom a PM text file, or click New Player\nto add players manually.'
curP=0
else:
PAnswers=[]
for x in players[curP].Answers: PAnswers.append(x)
for item in PAnswers: answers=answers+item+"\n"
combo = ttk.Combobox(edAW, textvariable=current_var, state="readonly", values=[item.Name for item in players])
if len(players)!=0:combo.current(curP)
combo.bind('<Up>', select_next) # up arrow
combo.bind('<Down>',select_next) # down arrow
combo.grid(row=0, column=1)
edAW.title("Edit Entries")
edALabel = Label(edAW, text="Player:")
edALabel.grid(row=0, column=0)
spacer = Label(edAW).grid(row=0, column=2)
edALabel = Label(edAW, text="Starting Score:", padx=10)
edALabel.grid(row=1, column=0)
edAText = Text(edAW)
if len(players)==0:
edAText.insert(INSERT,'Click Load... to load players and answers\nfrom a PM text file, or click New Player\nto add players manually.')
else:
edAText.insert(INSERT,answers)
edAText.grid(column=0, columnspan=3, rowspan=10, padx=5, pady=5)
edALoad = Button(edAW, text="Load", padx=20, command=lambda: edAL(edAText,combo)).grid(row=0,column=3,padx=10,pady=5)
edANP = Button(edAW, text="New Player", padx=4, command=lambda: newPlayer(edAText,combo, edAW)).grid(row=1,column=3,padx=10,pady=5)
edACN = Button(edAW, text="Change Name", padx=0,command=lambda: renamePlayer(edAText,combo, edAW)).grid(row=2, column=3, padx=10, pady=5)
edADP = Button(edAW, text="Delete Player", padx=0, command=lambda: delPlayer(edAText,combo)).grid(row=3, column=3, padx=10, pady=5)
edASave = Button(edAW, text="Save Changes", command=lambda: edPSave(edAW, edAText,combo)).grid(row=9, column=3,padx=10, pady=5)
edACancel = Button(edAW, text="Cancel", padx=20, command=lambda: edCancel(edAW)).grid(row=10, column=3,padx=10, pady=5)
edAW.rowconfigure(4, weight=1)
edAW.columnconfigure(2, weight=1)
return
def edQuestions(window):
window.withdraw()
myfile = "\n".join(item.Text.lstrip() for item in sg.Questions)
edQW = Toplevel(window)
edQW.title("Edit Questions")
edQLabel = Label(edQW,text="Click Load... to load the questions from a text file, or just type them in here one per line")
edQLabel.grid(row=0, columnspan=2)
edQText = Text(edQW)
edQText.insert(INSERT, myfile)
edQText.grid(column=0, rowspan=10, padx=5, pady=5)
edQLoad = Button(edQW, text="Load", padx=20, command=lambda: edQL(edQText)).grid(row=1, column=1)
edQSave = Button(edQW, text="Save Changes", command=lambda: edSave(edQW, edQText)).grid(row=9, column=1)
edQCancel = Button(edQW, text="Cancel", padx=20, command=lambda: edCancel(edQW)).grid(row=10, column=1)
return
def validate_entry(text):
return text.isdecimal()
def outPlayerscore():
print("[", end="")
for i in myPlayers:
if i != myPlayers[-1]:
print(i.score, end=",")
else:
print(i.score, end="")
print("]")
def dothis():
print("sg "+str(sg))
print("sg.dict"+str(sg.__dict__))
print("Questions[0]"+str(sg.Questions[0].__dict__))
print("Questions[0].Groups[0]" + str(sg.Questions[0].Groups[0].__dict__))
print("Players[0]" + str(sg.Players[0].__dict__))
def bDown(event):
tv = event.widget
if tv.identify_row(event.y) not in tv.selection():
tv.selection_set(tv.identify_row(event.y))
def bUp(event):
tv = event.widget
if tv.identify_row(event.y) == "": return
Tmoveto = tv.index(tv.identify_row(event.y))
if tv.identify_row(event.y) not in tv.selection():
moveto = myTreeview.item(tv.identify_row(event.y))
fromANS = myTreeview.item(tv.selection())['values']
fromGRP = myTreeview.item(tv.selection())['values']
toGRP = moveto['values']
if toGRP!="":
if fromGRP[0]==toGRP[0] and len(fromGRP)==3:
for i, q in enumerate(sg.Questions):
if int(i) == int(fromANS[0]):
myq=q
for g in myq.Groups:
if g.Text == fromGRP[1]:
fog = g
if g.Text == toGRP[1]:
tog = g
found=0
for g in myq.Groups:
for asx in g.Answers:
myans=asx
if asx._Player.Name==fromANS[2] and found!=1:
found=1
tog.Answers.append(asx)
fog.Answers.remove(asx)
if len (fog.Answers) == 0:
myq.Groups.remove(fog)
updateTreeview()
def displayTreeview():
global myTreeview
myTreeview.grid(row=1, column=0, columnspan=6, sticky='NSEW', padx=10, pady=5)
def updateTreeview():
global myTreeview,curQ,vsb
if len(sg.Questions) != 0:
#print("TreeView Updated")
myTreeview.grid_forget()
myTreeview.delete(*myTreeview.get_children())
if (curQ > len(sg.Questions)):
curQ == len(sg.Questions)
if (curQ < 1):
curQ = 1
curQuestion = sg.Questions[curQ - 1]
# give instructions if no players loaded
if (len(sg.Players) == 0):
myTreeview.insert('', 'end', text="Click sheep > Edit Entries... to add entries.", iid=0)
displayTreeview()
curgroup = 0
curItem = 0
# loop through each group
for group in curQuestion.Groups:
group_node = myTreeview.insert("", "end", open=cbvar1.get(), tags="group", value=[sg.Questions.index(curQuestion),group.Text],text=group.Text + f" - [{len(group.Answers)}]")
for answer in group.Answers:
myTreeview.insert(group_node, "end", tags="answer", value=[sg.Questions.index(curQuestion),group.Text,answer.Player.Name],text=answer.Text + " - " + answer.Player.Name)
displayTreeview()
else:
myTreeview.grid_forget()
vsb.grid_forget()
myTreeview.delete(*myTreeview.get_children())
displayTreeview()
# text will be added later so don't bother with it in this function
# for grp in curQuestion:
# curGroup=myTreeview.insert('','end',text=".",iid=0)
# myTreeview.insert('','end',text="this is text",iid=0)
def do_popup(event):
tv = event.widget
item = tv.identify_row(event.y)
if tv.identify_row(event.y) not in tv.selection():
tv.selection_set(tv.identify_row(event.y))
if item == "":
return
elif myTreeview.item(item)["tags"][0]=="group":
try:
tv.popup.tk_popup(event.x_root, event.y_root, 0)
finally:
tv.popup.grab_release()
elif myTreeview.item(item)["tags"][0]=="answer":
try:
tv.popup2.tk_popup(event.x_root, event.y_root, 0)
finally:
tv.popup2.grab_release()
def SetGroupName():
top = Toplevel()
top.grab_set()
top.bind('<Return>', lambda x: set_newgroupname(top,entry.get()))
label = Label(top, text="What is the new name for the group?")
entry = Entry(top, textvariable=input)
buttonok = Button(top, text="OK", command=lambda :set_newgroupname(top,entry.get()))
buttoncancel = Button(top, text="Cancel", command=lambda: edCancel(top))
label.pack()
entry.pack()
buttonok.pack()
buttoncancel.pack()
entry.focus_force()
def set_newgroupname(top,input):
top.destroy()
values=myTreeview.item(myTreeview.selection())['values']
q = values[0]
question = sg.Questions[q]
groupnames=[grp.Text for grp in question.Groups]
#print(groupnames)
if input not in groupnames and input!="":
sg.Questions[q].Groups[groupnames.index(str(values[1]))].Text=input
updateTreeview()
def UseAsGroupName():
values=myTreeview.item(myTreeview.selection())['values']
q = values[0]
question = sg.Questions[q]
groupnames = [grp.Text for grp in question.Groups]
#print(values)
#print(groupnames)
answers = [x for x in sg.Questions[q].Groups[groupnames.index(values[1])].Answers]
for x in answers:
if x.Player.Name==str(values[2]):
newgrpname = x.Text
if newgrpname not in groupnames and input!="":
sg.Questions[q].Groups[groupnames.index(str(values[1]))].Text = newgrpname
updateTreeview()
def MoveToNewGroup():
values = myTreeview.item(myTreeview.selection())['values']
q = values[0]
question = sg.Questions[q]
groupnames = [grp.Text for grp in question.Groups]
#print(values)
#print(groupnames)
answers = [x for x in sg.Questions[q].Groups[groupnames.index(values[1])].Answers]
for x in answers:
if x.Player.Name == str(values[2]):
newgrpname = x.Text
answer=x
if answer.Text not in groupnames and input != "":
newgroup = ShGame.ShGroup(sg.Questions[q],answer.Text)
newgroup.Answers.append(answer)
sg.Questions[q].Groups.append(newgroup)
sg.Questions[q].Groups[groupnames.index(str(values[1]))].Answers.remove(answer)
if len(sg.Questions[q].Groups[groupnames.index(str(values[1]))].Answers)==0:
sg.Questions[q].Groups.pop(groupnames.index(str(values[1])))
updateTreeview()
gt= {'Sheep':1,'PeehsDM':2, 'PeehsFB':3, 'PeehsHybrid':4, 'Heep':5, 'Heep15':6, 'Heep2':7, 'Kangaroo':8, 'Manual':9}
gtl=['filler','Sheep','PeehsDM','PeehsFB','PeehsHybrid','Heep','Heep15','Heep2','Kangaroo','Manual']
rt= {'None':0,'Up':1,'Down':2,'Nearest':3}
rtl=['None','Up','Down','Nearest']
bt= {'None':1, 'Add':2, 'Override':3}
btl=['filler','None', 'Add', 'Override']
sg = ShGame()
players=[]
score=[]
curQ=1
curP=1
window = Tk()
window.geometry("680x450")
window.title("Sheep Score Foggies Edition")
Outputtype = IntVar()
Outputtype.set(1)
Gametype = IntVar()
Gametype.set(1)
Roundtype = IntVar()
Roundtype.set(0)
current_var = StringVar()
menubar = Menu(window)
cbvar1 = IntVar()
window.config(menu=menubar)
fileMenu=Menu(menubar,tearoff=0)
menubar.add_cascade(label="File",menu=fileMenu)
fileMenu.add_command(label="New Reveal", command=resetProgram)
fileMenu.add_command(label="Load Reveal...", command=sg.loadReveal)
fileMenu.add_command(label="Save Reveal...", command=sg.saveReveal)
fileMenu.add_command(label="Debug", command=dothis)
fileMenu.add_separator()
fileMenu.add_command(label="Exit",command=quit)
sheepMenu = Menu(menubar,tearoff=0)
menubar.add_cascade(label="Sheep",menu=sheepMenu)
sheepMenu.add_command(label="Edit Questions...", command=lambda:edQuestions(window))
sheepMenu.add_command(label="Edit Entries...", command=lambda:edAnswers(window))
scoringMenu=Menu(menubar,tearoff=0)
sheepMenu.add_cascade(label="Scoring",menu=scoringMenu)
scoringMenu.add_radiobutton(label="Sheep",value=1, variable=Gametype)
peehsMenu=Menu(menubar,tearoff=0)
scoringMenu.add_cascade(label="Peehs",menu=peehsMenu)
peehsMenu.add_radiobutton(label="DM Scoring",value=2, variable=Gametype)
peehsMenu.add_radiobutton(label="FB Scoring",value=3, variable=Gametype)
peehsMenu.add_radiobutton(label="Hybrid",value=4, variable=Gametype)
heepsMenu=Menu(menubar,tearoff=0)
scoringMenu.add_cascade(label="Heeps",menu=heepsMenu)
heepsMenu.add_radiobutton(label="2x Heep Bonus",value=7, variable=Gametype)
heepsMenu.add_radiobutton(label="1.5x Heep Bonus",value=6, variable=Gametype)
heepsMenu.add_radiobutton(label="No Heep Bonus",value=5, variable=Gametype)
scoringMenu.add_radiobutton(label="Kangaroo",value=8, variable=Gametype)
scoringMenu.add_radiobutton(label="Manual",value=9, variable=Gametype)
scoringMenu.add_separator()
roundingMenu=Menu(menubar,tearoff=0)
scoringMenu.add_cascade(label="Rounding",menu=roundingMenu)
roundingMenu.add_radiobutton(label="No Rounding",value=0, variable=Roundtype)
roundingMenu.add_radiobutton(label="Round Up",value=1, variable=Roundtype)
roundingMenu.add_radiobutton(label="Round Down",value=2, variable=Roundtype)
roundingMenu.add_radiobutton(label="Round Nearest",value=3, variable=Roundtype)
outputMenu=Menu(menubar,tearoff=0)
menubar.add_cascade(label="Output",menu=outputMenu)
outputMenu.add_command(label="Copy answers for this question")
outputMenu.add_command(label="Copy total scores up to this question")
outputMenu.add_command(label="Copy score table up to this question")
outputMenu.add_command(label="Copy Player list")
outputMenu.add_separator()
styleMenu=Menu(menubar,tearoff=0)
outputMenu.add_cascade(label="Output Style",menu=styleMenu)
styleMenu.add_radiobutton(label="Forum Table",value=1, variable=Outputtype)
styleMenu.add_radiobutton(label="Forum Formatted Text",value=2, variable=Outputtype)
styleMenu.add_radiobutton(label="Unformatted Text",value=3, variable=Outputtype)
qButton1 = Button(window, text="<",command=qdown).grid(row=0,column=1)
qButton2 = Button(window, text=">",command=qup).grid(row=0,column=3)
myLabel1 = Label(window, text="Q #", padx=5).grid(row=0,column=0)
myCheckbox1 = Checkbutton(window, text='show colapsed', variable=cbvar1, onvalue=0, offvalue=1, command=updateTreeview)
myCheckbox1.grid(row=0,column=4)
myLabel2 = Label(window, text="Click Sheep > Edit Questions... to begin.")
myLabel2.grid(row=0,column=5)
myTextbox1 = Entry(window,width=4 ,validate="key",
validatecommand=(window.register(validate_entry), "%S"))
myTextbox1.insert(INSERT,curQ)
myTextbox1.grid(row=0,column=2)
myTreeview = ttk.Treeview(window, show="tree")
vsb = ttk.Scrollbar(window, orient="vertical", command=myTreeview.yview)
myTreeview.configure(yscrollcommand=vsb.set)
vsb.grid(column=6, sticky='ns')
myTreeview.popup = Menu(window, tearoff=0)
myTreeview.popup.add_command(label="Set Group Name...", command=SetGroupName) # , command=next) etc...
myTreeview.popup.add_command(label="Mark invalid")
myTreeview.popup.add_command(label="Group Score") # , command=lambda: self.closeWindow())
myTreeview.popup2 = Menu(window, tearoff=0)
myTreeview.popup2.add_command(label="Use as Group Name", command=UseAsGroupName) # , command=next) etc...
myTreeview.popup2.add_command(label="Move to new group", command=MoveToNewGroup)
myTreeview.popup2.add_command(label="Player Score...") # , command=lambda: self.closeWindow()
myTreeview.bind("<Button-3>", do_popup)
myTreeview.bind("<ButtonPress-1>", bDown)
myTreeview.bind("<ButtonRelease-1>", bUp, add='+')
displayTreeview()
vsb = ttk.Scrollbar(window,orient="vertical" ,command=myTreeview.yview)
window.rowconfigure(1, weight=1)
window.columnconfigure(5, weight=1)
#sg.loadReveal()
window.mainloop()
RE: Sheepulator in python - Neowise - 15-07-2023
Here is a sample reveal that you can load into the program.
Names have been changed as I did not ask the players if I could share the origional.
rename file to reveal.sheep17 after its downloaded.
RE: Sheepulator in python - Neowise - 15-07-2023
If you go to this site,
https://trinket.io/features/pygame
click in the code for the star pusher game and select all and press back-space key on your keyboard, or however you delete text on your mobile.
copy the code from any main.py code i have shared ( starts with from 'tkinter import *' above) and paste into main.py.
delete the starPusherLevels.txt file by clicking on the cog, then the trash can.
upload the reveal file, if you wish to load it in the program, using the upload files txt file button. ( the forum does not accept a .sheep17 file at the moment so I had to change it to txt.
if you rename this back to reveal.sheep17 before uploading you wont need to do the loading trick
Then click run.
I'll just explain the loading trick, then let you explore the tool for yourself.
Click load reveal, the program only looks for .sheep17 file, so in filename type *.txt and click open. now you can see the uploaded txt file. click to load.
have fun.
I don't know how to save reaveals yet using the pygame tricket, if and when i figure it out, i'll let you know.
RE: Sheepulator in python - Neowise - 15-07-2023
If you are a windows user, here is the program so far, don't expect too much.
RE: Sheepulator in python - E.N. - 15-07-2023
Good work Neo. Just on mobile, but I'll have a nose next time I'm on the computer
Is this just an exercise for yourself to see if you can? Or are you hoping it'll be better than the current version we use?
RE: Sheepulator in python - Neowise - 15-07-2023
(15-07-2023, 04:18 PM)E.N. Wrote: Good work Neo. Just on mobile, but I'll have a nose next time I'm on the computer
Is this just an exercise for yourself to see if you can? Or are you hoping it'll be better than the current version we use?
Exercise for myself + I can maintain something I understand easier that code i don't really understand. I'll probably know more about c# after this exercise is over, but I think python is just easier to read.
RE: Sheepulator in python - Neowise - 16-07-2023
Latest code is very near complete.
To Do:
to add incorrect/invalid functionality. right click answer
to addgroup bonus functionality. right click group
to add merge groups functionality. drag and drop group onto group.
Anything that is not these, let me know, as its not on my radar.
and i've gone over the limit allowed for posting code,
so here is a link to it instead.
https://github.com/sKeiths/SheepScorer/blob/main/SheepScorer.py
Quote:Please correct the following errors before continuing:
The message is too long. Please enter a message shorter than 65535 characters (currently 69906).
RE: Sheepulator in python - Neowise - 17-07-2023
Latest code is very near complete.
To Do:
to add incorrect/invalid functionality. right click answer
to addgroup bonus functionality. right click group and player
to add merge groups functionality. drag and drop group onto group.
Anything that is not these, let me know, as its not on my radar.
https://github.com/sKeiths/SheepScorer/raw/main/dist/SheepScorer.exe
https://github.com/sKeiths/SheepScorer/blob/main/SheepScorer.py
RE: Sheepulator in python - Neowise - 18-07-2023
And it's finished.
Please let me know of any bugs you find.
(17-07-2023, 10:41 PM)Neowise Wrote: Latest code is very near complete.
To Do:
to add incorrect/invalid functionality. right click answer
to addgroup bonus functionality. right click group and player
to add merge groups functionality. drag and drop group onto group.
Anything that is not these, let me know, as its not on my radar.
https://github.com/sKeiths/SheepScorer/raw/main/output/SheepScorer.exe
https://github.com/sKeiths/SheepScorer/blob/main/SheepScorer.py
RE: Sheepulator in python - Neowise - 19-07-2023
Questions can be loaded in from a text file, one question per line.
Answer can be loaded in for an exported messages file from forumuofgames.
Game host can create a new mail folder and move all game messages into it.
Then when ready to transfer, click download messages and change folders to the new folder nae.
change export message age to 0 days.
change export unread messages to yes.
change to save to text file.
On my machine, it opened the data in my browser and didn't save a text file to my machine. I then copied the contents of the screen and saved into a new text file. this is the file I load using the load option in answers.
When loaded, insure first lines in messages are questions (not hi or something like that) for every message before pressing save.
Currently, if the is more answers than current questions in the questions form, it will add new questions. this makes the save file incompatible with original sheep scorer. I can change this behaviour to strip all lines after questions have ended, like old sheep scorer does.
Messages by default are sorted with newest first, so those who wish to change there answer can resubmit and their latest message will be used by default. all older messages will be ignored, that is, no duplicate entries will be added.
|