# Copyright 2008 by Peter Moxhay and Wade Brainerd. # This file is part of Math. # # Math is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Math is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Math. If not, see . from objectarea import ObjectArea, Color from vector import Vector from shapeobject import ShapeObject from symbolobject import SymbolObject from instructionsobject import InstructionsObject from problem import Problem import gtk, math, random class LengthProblem(Problem): """ Generates a problem in which two lengths are compared. """ def __init__(self, container, color_scheme, (letter1, letter2) ): self.container = container self.color_scheme = color_scheme self.letter1 = letter1 self.letter2 = letter2 self.problem_number = -1 self.generate_problem() self.show_problem() self.answer = self.find_answer() self.container.moons_visible = False def generate_problem(self): # Set the colors. if self.color_scheme == 'red_green': (color1, color2) = random.choice([(Color.RED, Color.GREEN), (Color.GREEN, Color.RED)]) elif self.color_scheme == 'green_blue': (color1, color2) = random.choice([(Color.GREEN, Color.BLUE), (Color.BLUE, Color.GREEN)]) else: (color1, color2) = random.choice([(Color.RED, Color.BLUE), (Color.BLUE, Color.RED)]) # Some rectangles of different length. LENGTH_0 = [ Vector(0, 0), Vector(50, 0), Vector(50, 175), Vector(0, 175) ] LENGTH_1 = [ Vector(0, 0), Vector(50, 0), Vector(50, 200), Vector(0, 200) ] LENGTH_2 = [ Vector(0, 0), Vector(50, 0), Vector(50, 225), Vector(0, 225) ] LENGTH_3 = [ Vector(0, 0), Vector(50, 0), Vector(50, 250), Vector(0, 250) ] LENGTH_4 = [ Vector(0, 0), Vector(50, 0), Vector(50, 275), Vector(0, 275) ] LENGTH_5 = [ Vector(0, 0), Vector(50, 0), Vector(50, 300), Vector(0, 300) ] LENGTH_6 = [ Vector(0, 0), Vector(50, 0), Vector(50, 325), Vector(0, 325) ] LENGTH_7 = [ Vector(0, 0), Vector(50, 0), Vector(50, 350), Vector(0, 350) ] LENGTH_8 = [ Vector(0, 0), Vector(50, 0), Vector(50, 375), Vector(0, 375) ] LENGTH_9 = [ Vector(0, 0), Vector(50, 0), Vector(50, 400), Vector(0, 400) ] LENGTH_10 = [ Vector(0, 0), Vector(50, 0), Vector(50, 425), Vector(0, 425) ] LENGTH_11 = [ Vector(0, 0), Vector(50, 0), Vector(50, 450), Vector(0, 450) ] LENGTH_12 = [ Vector(0, 0), Vector(50, 0), Vector(50, 475), Vector(0, 475) ] LENGTH_13 = [ Vector(0, 0), Vector(50, 0), Vector(50, 500), Vector(0, 500) ] LENGTH_14 = [ Vector(0, 0), Vector(50, 0), Vector(50, 525), Vector(0, 525) ] LENGTH_15 = [ Vector(0, 0), Vector(50, 0), Vector(50, 550), Vector(0, 550) ] # Standard initial positions for the shapes. upper_left_position = Vector(300, 300) lower_right_position = Vector(900, 400) upper_right_position = Vector(900, 300) lower_left_position = Vector(300, 400) # Randomize the initial positions of the shapes. (original_position1, original_position2) = random.choice([(upper_left_position, lower_right_position), \ (lower_right_position, upper_left_position), \ (upper_right_position, lower_left_position), \ (lower_left_position, upper_right_position)]) # Randomize the initial angles of the shapes. (original_angle1, original_angle2) = random.choice( [(0, math.pi/4), (math.pi/4, 0) , \ (0, math.pi/4), (math.pi/4, 0), (0, math.pi/4), (math.pi/4, 0), (0, 0), (math.pi/2, 0), (0, math.pi/2) ]) # The total number of problems. self.n_problems = 18 # Choose a random problem. while (self.problem_number in self.container.recently_used): self.problem_number = random.randrange(0, self.n_problems) # Uncomment to test a particular problem. #self.problem_number = 3 # Define the various problems. if self.problem_number == 0: object1 = LENGTH_1 object2 = LENGTH_1 elif self.problem_number == 1: object1 = LENGTH_3 object2 = LENGTH_3 elif self.problem_number == 2: object1 = LENGTH_5 object2 = LENGTH_5 elif self.problem_number == 3: object1 = LENGTH_1 object2 = LENGTH_3 elif self.problem_number == 4: object1 = LENGTH_2 object2 = LENGTH_4 elif self.problem_number == 5: object1 = LENGTH_7 object2 = LENGTH_7 elif self.problem_number == 6: object1 = LENGTH_3 object2 = LENGTH_5 elif self.problem_number == 7: object1 = LENGTH_4 object2 = LENGTH_6 elif self.problem_number == 8: object1 = LENGTH_5 object2 = LENGTH_7 elif self.problem_number == 9: object1 = LENGTH_6 object2 = LENGTH_8 elif self.problem_number == 10: object1 = LENGTH_9 object2 = LENGTH_9 elif self.problem_number == 11: object1 = LENGTH_7 object2 = LENGTH_9 elif self.problem_number == 12: object1 = LENGTH_8 object2 = LENGTH_10 elif self.problem_number == 13: object1 = LENGTH_9 object2 = LENGTH_11 elif self.problem_number == 14: object1 = LENGTH_10 object2 = LENGTH_12 elif self.problem_number == 15: object1 = LENGTH_11 object2 = LENGTH_11 elif self.problem_number == 16: object1 = LENGTH_11 object2 = LENGTH_13 elif self.problem_number == 17: object1 = LENGTH_0 object2 = LENGTH_2 else: object1 = LENGTH_1 object2 = LENGTH_1 # Switch the shapes half the time (so we get > as well as < problems). if random.choice([0,1]) == 0: self.shape1 = ShapeObject(color1, self.letter1, object1, original_position1, original_angle1) self.shape2 = ShapeObject(color2, self.letter2, object2, original_position2, original_angle2) else: self.shape1 = ShapeObject(color1, self.letter1, object2, original_position1, original_angle1) self.shape2 = ShapeObject(color2, self.letter2, object1, original_position2, original_angle2) return def show_problem(self): self.container.configure_dragging_area(25, 48, 32, math.pi/8) self.container.add_object(self.shape1) self.container.add_object(self.shape2) # Randomize which object is initially selected. if random.choice([0,1]) == 0: self.container.select_object(self.shape1) else: self.container.select_object(self.shape1) # Add letter symbols. self.container.letter1 = SymbolObject(Vector(500 + 400 - 50, 650), self.shape1.symbol, None, None, size=100) self.container.letter2 = SymbolObject(Vector(700 + 400 - 50, 650), self.shape2.symbol, None, None, size=100) self.container.letter1.draggable = False self.container.letter1.selectable = False self.container.letter2.draggable = False self.container.letter2.selectable = False self.container.add_object(self.container.letter1) self.container.add_object(self.container.letter2) self.container.questionmark = SymbolObject(Vector(600 + 400 - 50, 650), '?', None, None, size=80) self.container.questionmark.draggable = False self.container.questionmark.selectable = False self.container.add_object(self.container.questionmark) self.container.instructions = InstructionsObject(Vector(50, 25), 'Compare the things in length') self.container.add_object(self.container.instructions) def scaled(self, vectors, factor): for vector in vectors: new_vectors = [v.scaled(factor) for v in vectors] return new_vectors def larger(self, vectors): for vector in vectors: new_vectors = [v.scaled(1.2) for v in vectors] return new_vectors def smaller(self, vectors): for vector in vectors: new_vectors = [v.scaled(0.8) for v in vectors] return new_vectors def is_shape_1_vertical(self): if abs(self.shape1.angle % math.pi) < 0.01: return True else : return False def is_shape_1_horizontal(self): if abs((self.shape1.angle + math.pi/2.) % math.pi) < 0.01: return True else : return False def is_shape_2_vertical(self): if abs(self.shape2.angle % math.pi) < 0.01: return True else : return False def is_shape_2_horizontal(self): if abs((self.shape2.angle + math.pi/2.) % math.pi) < 0.01: return True else : return False def check_problem_solved(self): #print "" #print "Length Problem: check_problem_solved called" # Make sure the two ShapeObjects both have four points. if len(self.shape1.points) != 4 or len(self.shape2.points) != 4: return False # What are the angles? #angle1 = self.shape1.angle #angle1_mod_pi = angle1 % (math.pi) #print "angle1 =",angle1 #print "angle1_mod_pi =",angle1_mod_pi #angle2 = self.shape2.angle #angle2_mod_pi = angle2 % (math.pi) #print "angle2 =",angle2 #print "angle2_mod_pi =",angle2_mod_pi # #if abs(angle1_mod_pi_over_2) > 0.01 or abs(angle2_mod_pi_over_2) > 0.01: # print "rejected because length is neither horizontal nor vertical" # return False b_ver1 = self.is_shape_1_vertical() b_hor1 = self.is_shape_1_horizontal() #if b_ver1: # print "Shape", self.shape1.symbol,"is vertical" #elif b_hor1: # print "Shape", self.shape1.symbol,"is horizontal" b_ver2 = self.is_shape_2_vertical() b_hor2 = self.is_shape_2_horizontal() #if b_ver2: # print "Shape", self.shape2.symbol,"is vertical" #elif b_hor2: # print "Shape", self.shape2.symbol,"is horizontal" #if b_ver1 and b_ver2: # print "Both shapes are vertical" #if b_hor1 and b_hor2: # print "Both shapes are vertical" if (not (b_ver1 and b_ver2)) and (not (b_hor1 and b_hor2)): #print "Shapes are neither both vertical nor both horizontal." return False # First, find out how many points coincide. p0 = self.shape1.points p0 = [self.shape1.transform_point(p) for p in p0] p1 = self.shape2.points p1 = [self.shape2.transform_point(p) for p in p1] # Sort the points so they can be compared consistently. def sort_points_arbitrarily(a, b): if a.x != b.x: return cmp(a.x, b.x) else: return cmp(a.y, b.y) p0 = sorted(p0, cmp=sort_points_arbitrarily) p1 = sorted(p1, cmp=sort_points_arbitrarily) coords_of_equal_points = [] n_equal = 0 for i in range(0,len(p0)): for j in range(0,len(p1)): #print "p0[i] =", p0[i] #print "p1[j] =", p1[j] if p0[i].approx_equal(p1[j]): n_equal += 1 coords_of_equal_points.append(p0[i]) #print "n_equal =", n_equal #for i in range (0, len(coords_of_equal_points)): # print "coords_of_equal_points =",coords_of_equal_points[i] if self.answer == 'equal' and n_equal == 2: #print "distance between equal points =",(coords_of_equal_points[0] - coords_of_equal_points[1]).length() # Problem not solved if length are compared end-to-end. if (coords_of_equal_points[0] - coords_of_equal_points[1]).length() < 55: return False else: return True elif (self.answer == 'less' or self.answer == 'greater') and n_equal == 1: if b_ver1 and b_ver2: if self.are_y_bounds_correct(): return True elif b_hor1 and b_hor2: if self.are_x_bounds_correct(): return True return False def are_x_bounds_correct(self): shape_1_x_bounds = self.shape1.bounds_min.x, self.shape1.bounds_max.x shape_2_x_bounds = self.shape2.bounds_min.x, self.shape2.bounds_max.x tolerance = 5 # Does shape2 fit horizontally within shape 1? if shape_2_x_bounds[0] >= shape_1_x_bounds[0] - tolerance and shape_2_x_bounds[1] <= shape_1_x_bounds[1] + tolerance: return True # Does shape2 fit horizontally within shape 2? elif shape_1_x_bounds[0] >= shape_2_x_bounds[0] - tolerance and shape_1_x_bounds[1] <= shape_2_x_bounds[1] + tolerance: return True else: return False # Test whether two vertical shapes are aligned correctly for a comparison. def are_y_bounds_correct(self): shape_1_y_bounds = self.shape1.bounds_min.y, self.shape1.bounds_max.y shape_2_y_bounds = self.shape2.bounds_min.y, self.shape2.bounds_max.y tolerance = 5 # Does shape2 fit vertically within shape 1? if shape_2_y_bounds[0] >= shape_1_y_bounds[0] - tolerance and shape_2_y_bounds[1] <= shape_1_y_bounds[1] + tolerance: return True # Does shape2 fit vertically within shape 2? elif shape_1_y_bounds[0] >= shape_2_y_bounds[0] - tolerance and shape_1_y_bounds[1] <= shape_2_y_bounds[1] + tolerance: return True else: return False #def point_of_one_shape_lies_on_side_of_the_other(self): # # Fill a list with the sides of shape 1. # sides = [] # for m in range(0, len(self.shape1.points) - 1): # sides.append( (self.shape1.points[m], self.shape1.points[m + 1]) ) # sides.append( (self.shape1.points[len(self.shape1.points) - 1], self.shape1.points[0]) ) # # # Check whether a point of shape 2 lies on a side of shape 1. # for i in range(0, len(self.shape2.points) - 1): # for j in range(0, len(sides)): # dist = self.distance_point_from_line(self.shape2.points[i], sides[j]) # if dist < 0.01 and self.is_between_end_points(self.shape2.points[i], sides[j]): # return True # # # Fill a list with the sides of shape 2. # sides = [] # for m in range(0, len(self.shape2.points) - 1): # sides.append( (self.shape2.points[m], self.shape1.points[m + 1]) ) # sides.append( (self.shape2.points[len(self.shape2.points) - 1], self.shape2.points[0]) ) # # # Check whether a point of shape 1 lies on a side of shape 2. # for i in range(0, len(self.shape1.points) - 1): # for j in range(0, len(sides)): # dist = self.distance_point_from_line(self.shape1.points[i], sides[j]) # if dist < 0.01 and self.is_between_end_points(self.shape1.points[i], sides[j]): # return True # # return False #def is_between_end_points(self, point, (point0, point1)): # if point1.x > point0.x: # if point.x > point0.x and point.x < point1.x: # return True # else: # return False # elif point1.x < point0.x: # if point.x > point1.x and point.x < point0.x: # return True # else: # return False # else: # if point1.y > point0.y: # if point.y > point0.y and point.y < point1.y: # return True # elif point1.y < point0.y: # if point.y > point1.y and point.y < point0.y: # return True # else: # return False # # return False # #def distance_point_from_line(self, point, (point0, point1)): # dist = abs( ( (point0.y - point1.y)*point.x + (point1.x - point0.x)*point.y + (point0.x*point1.y - point1.x*point0.y)) \ # / math.sqrt( (point1.x - point0.x) ** 2 + (point1.y - point0.y) ** 2) ) # return dist def find_answer(self): if self.shape1.area > self.shape2.area: self.answer = 'greater' elif self.shape1.area < self.shape2.area: self.answer = 'less' else: self.answer = 'equal' return self.answer