Skip to content
20 changes: 10 additions & 10 deletions dump.sql

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions game.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,16 @@ def playGame(self, number_of_objects):
question_answers = {}
questions_asked = {}
objlist = objects.get_all()
# For each object in the set
count = 0

# quit variable passed to main that tells if user wants to quit
quit = False

# For each object in the set
for i in objlist:
if robot():
robot().initGazeCounts()

if config.args.notsimulated:
for j in range(len(objlist)):
print objlist[j].name
Expand All @@ -68,16 +74,18 @@ def playGame(self, number_of_objects):
count += 1

if config.args.notsimulated == True:
quit = interface.ask("Would you like to quit this game early? \nThere are %d rounds left. " % (17 - count))
if quit == "yes":
quit = not interface.ask("Do you want to play again?")
# quit = interface.ask("Would you like to quit this game early? \nThere are %d rounds left. " % (17 - count))
if quit:
break

# Save results
self._record(round_wins, round_losses, NoOfQuestions, count)

end = time.time()
log.info("Game %d complete (Took %ds)", self.id, int(end - start))

return round_wins, round_losses, NoOfQuestions, avg_win, avg_lose, question_answers, questions_asked
return round_wins, round_losses, NoOfQuestions, avg_win, avg_lose, question_answers, questions_asked, quit

def _record(self, wins, losses, num_questions, number_of_objects_played):
"""
Expand Down
5 changes: 3 additions & 2 deletions interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
def ask(question):
if robot():
return robot().ask(question)

else:
while True:
response = raw_input(question).lower()[0]
if response == "y":
return "yes"
return True
elif response == "n":
return "no"
return False

def say(text):
if robot():
Expand Down
67 changes: 53 additions & 14 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import os
import time
import sys
import math
import logging as log
import argparse
import traceback

config = None

Expand Down Expand Up @@ -34,21 +37,13 @@ def __init__(self):
if config.args.setup:
self.setup()

if robot.robot():
log.info("Robot is counting objects")
self.number_of_objects = robot.robot().count_objects()
robot.robot().say("I see %d objects" % self.number_of_objects, False)
log.info("%d objects detected", self.number_of_objects)
self.number_of_objects = 17

# runtime does not include the time it took to run setup since it should only be run once
start = time.time()
self.simulate()
end = time.time()

log.info('Simulation complete! (Took %ds)', int(end - start))


def simulate(self):
"""
Run the simulation
Expand All @@ -66,13 +61,42 @@ def simulate(self):
questions_asked = {}
question_answers = {}

# TODO: Noelle - calibrate your gaze tracker here, unless it's better to do it at the beginning of every game/round
if robot.robot():

# # count objects
# log.info("Robot is counting objects")
# self.object_angles = robot.robot().count_objects()
# for obj in self.object_angles:
# print "OBJECT"
# print "\t", obj
# self.number_of_objects = len(self.object_angles)
# robot.robot().say("I see %d objects" % self.number_of_objects, False)
# log.info("%d objects detected", self.number_of_objects)
# # log.info("Objects detected at the following angles: " + str(self.object_angles))
# self.number_of_objects = 17
# robot.robot().rest()

# temporary manual input of object angles (obj 1 -> obj 17 from robot's right to left in 2 staggered rows)
# self.object_angles = [[index * 0.123 - math.pi/3, 0] for index in range(17)]

raw_angles = [-54]*3 + [-36]*3 + [-18]*3 + [18]*3 + [36]*3 + [54]*2
self.object_angles = [[math.radians(angle), 0] for angle in raw_angles]

# start face tracking and initialize gaze tracking
robot.robot().wake(0.7)
time.sleep(0.5)
robot.robot().trackFace()

if config.args.gaze:
time.sleep(0.5)
robot.robot().recordObjectAngles(self.object_angles)
robot.robot().findPersonPitchAdjustment()

for number in range(16, 31):
# TODO: make the number of games configurable??
game = Game(number)

game_wins, game_losses, game_num_questions, game_win_avg, game_lose_avg, game_answers, game_questions = game.playGame(self.number_of_objects)
game_wins, game_losses, game_num_questions, game_win_avg, game_lose_avg, game_answers, game_questions, quit = game.playGame(self.number_of_objects)

# dictionaries with complete list of questions asked and the corresponding answers
questions_asked[game.id] = game_questions
Expand All @@ -90,8 +114,8 @@ def simulate(self):
models.build(game, 3, self.number_of_objects, questions_asked, question_answers)

if config.args.notsimulated:
quit = interface.ask("Would you like to stop playing completely? \nThere are %d games left. " % (30 - number))
if quit == "yes":
# quit = interface.ask("Would you like to stop playing completely? \nThere are %d games left. " % (30 - number))
if quit:
break

log.info("Overall Wins: %d Overall Losses: %d", wins, losses)
Expand All @@ -103,6 +127,9 @@ def simulate(self):

if config.args.robot:
# TODO: remove this when we fix the robot class
robot.robot().stopTrackingFace()
robot.robot().gaze.unsubscribe("_")
robot.robot().rest()
robot.broker.shutdown()

def setup(self):
Expand Down Expand Up @@ -164,13 +191,18 @@ def _config():
parser.add_argument("-t", "--socket", help="path to MySQL socket", default=config.db["socket"])
parser.add_argument("-r", "--robot", action="store_true", help="runs code using robot")
parser.add_argument("--address", help="the robot's ip address", default=config.robot["address"])
parser.add_argument("-g", "--gaze", action="store_true", help="uses person's gaze to help guess object")

args = parser.parse_args()

# if you're using the robot then by default you have to provide the answers
if args.robot:
#if you're using the robot then by default you have to provide the answers
args.notsimulated = True

# if you're not using the robot then you can't use gaze analysis
else:
args.gaze = False

config.args = args

return config
Expand All @@ -183,4 +215,11 @@ def _config():
import database as db
import robot
import interface
Main()

try:
Main()

except Exception as e:
print traceback.format_exc()
if config.args.robot:
robot.robot().rest()
52 changes: 37 additions & 15 deletions objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,46 @@ def playObject(self, game, Pi, number_of_objects):
answer_data = get_all_answers(number_of_objects)
NoOfQuestions = 0

#at the very beginning of a round, each object has an equal chance of getting picked
# at the very beginning of a round, each object has an equal chance of getting picked
pO = np.array([1/float(number_of_objects)] * number_of_objects)

askedQuestions = []
answers = []
split = 0

while np.sort(pO)[pO.size - 1] - np.sort(pO)[pO.size - 2] < 0.15 and len(askedQuestions) < 15:
difference_threshold = 0.15

# while the best guess is less than 15% probability away from the 2nd-best guess and we've asked fewer than 15 questions
while np.sort(pO)[pO.size - 1] - np.sort(pO)[pO.size - 2] < difference_threshold and len(askedQuestions) < 15:
# Find best question (aka gives most info)
best_question = questions.get_best(game, objects, askedQuestions, pO, Pi, split, number_of_objects)
# Save under questions already asked
askedQuestions.append(best_question)
# Get updated probabilies based on the answer to the question
# Ask question and update probabilies based on the answer
pO, answers = questions.ask(best_question, self, game, answers, pO, Pi, objects, number_of_objects, answer_data)
if config.args.gaze:
gaze_confidences = robot().gazeConfidences()

gaze_weight = 0.7
print "pO:",
for p in pO:
print round(p, 2) * 100, ' ',
print
pO = np.array([(question_prob * (1 - gaze_weight)) + (question_prob * gaze_prob * gaze_weight) for question_prob, gaze_prob in zip(pO, gaze_confidences)])
print "Gp:",
for p in pO:
print round(p, 2) * 100, ' ',
print

# Split the current subset into two more subsets
split = questions.get_subset_split(pO, number_of_objects)
log.info('Finished asking %d questions', len(askedQuestions))

# Get most likely object
minimum=np.max(pO)
itemindexes =[i for i,x in enumerate(pO) if x==minimum]
A = np.asarray([[o] for o in get_all()])
guess = A[itemindexes][0][0]
itemindexes = [i for i,x in enumerate(pO) if x==minimum]
objects = np.asarray([[obj] for obj in get_all()])
guess = objects[itemindexes][0][0]

# Guess object (Compare what the system thinks is most likely to object currenly in play)
result = self._guess_object(guess)
Expand All @@ -64,9 +81,9 @@ def playObject(self, game, Pi, number_of_objects):

def gen_init_prob(self, number_of_objects):
"""
Fetches the proportions of yes answers
Returns a list containing sub-lists, each corresponding to an object
Each sub-list contains 289 tuples, one per question
Fetches proportions of yes answers for question/object combos
Returns a list containing a sublist for each object
Each object's sub-list contains 289 tuples, one for each question
Each tuple is in the form of (yes_answers, total_answers)
"""

Expand Down Expand Up @@ -110,7 +127,6 @@ def _record_results(self, game, game_answers, game_questions, guess, result, num
else:
result = 'win'


# TODO: clean up all the text files because this is kind of ridiculous
with open("game.txt", "a") as myfile:
myfile.write(str(game.id)+','+ str(self.id) +','+ str(guess.name)+"," + str(self.name) + "," + str(len(game_questions)) + "," + result + "\n")
Expand All @@ -127,6 +143,7 @@ def _guess_object(self, guess):
"""
Compare the object that the system thinks is most likely to the object currently in play
"""

if config.args.notsimulated:
self.id, self.name = get_actual(guess)
if self.id == guess.id:
Expand All @@ -136,6 +153,7 @@ def _guess_object(self, guess):
log.info('Lose [Guess: %s | Actual: %s]', guess.name, self.name)
return 0


def __init__(self, id, name):
# assigns an ID as a placeholder because we can't read the person's mind
# in order to assign an id for the purposes of initialization
Expand All @@ -150,14 +168,14 @@ def get(object_id):
Get a specific object based on id
"""

global _objects
return get_all()[object_id-1]


def get_all():
"""
Returns a list of all objects
"""

# TODO: make sure new objects have already been written into the database
global _objects
if not _objects:
Expand All @@ -166,15 +184,16 @@ def get_all():
_objects.append(Object(obj[0], obj[1]))
return _objects


def get_actual(guess):
"""
Returns the object the human player was thinking of
"""

global _objects
yn = interface.ask("My guess is %s. Was I right? " % guess.name)
answer = interface.ask("My guess is %s. Was I right? " % guess.name)

if yn == "yes":
if answer:
obj_name = guess.name
obj_id = guess.id
else:
Expand All @@ -196,8 +215,10 @@ def get_actual(guess):
if check == True:
break
obj_name = raw_input("It seems as though you mistyped. Please try typing the name of your object again. ")

return obj_id, obj_name


def get_all_answers(number_of_objects):
"""
Returns a list of sublists pertaining to each game
Expand All @@ -209,7 +230,7 @@ def get_all_answers(number_of_objects):
if not _answers:
_answers = []

db.cursor.execute('SELECT answer FROM Answers')
db.cursor.execute('SELECT answer FROM reference_answers')
questionanswers = db.cursor.fetchall()

answercnt = 0
Expand All @@ -219,7 +240,8 @@ def get_all_answers(number_of_objects):
for objcnt in range(17):
_answers[gamecnt].append([])
for tagcnt in range(289):
_answers[gamecnt][objcnt].append(int(questionanswers[answercnt][0]))
answer = int(questionanswers[answercnt][0])
_answers[gamecnt][objcnt].append(answer)
answercnt += 1

# total_length = 0
Expand Down
Loading