def line_complete(self, lines): """ Checks for an incomplete triangle in lines and returns the line that will make it complete as well as the number of triangles that will be completed (1 or 2). Note that since I know that the lines I create are valid, I do not use line_is_valid in this method. """ for l in lines: ### line is horizontal if l[0] == l[2]: # UPPER l_neg = [l[0]-1, l[1], l[2], l[3]] l_pos = [l[0], l[1], l[0]-1, l[1]+1] l_v = [l[0]-1, l[1], l[0], l[1]] r_v = [l[2]-1, l[3], l[2], l[3]] # negative slope exist if l_neg in lines and not line_overlaps(l_v, lines): if self.incomplete_exist(l_v, lines, "ver_left"): return l_v, 2 return l_v, 1 # positive slope exist elif l_pos in lines and not line_overlaps(r_v, lines): if self.incomplete_exist(r_v, lines, "ver_right"): return r_v, 2 return r_v, 1 # left vertical line exist elif l_v in lines and not line_overlaps(l_neg, lines): if self.incomplete_exist(l_neg, lines, "neg_top"): return l_neg, 2 return l_neg, 1 # right vertical line exist elif r_v in lines and not line_overlaps(l_pos, lines): if self.incomplete_exist(l_pos, lines, "pos_top"): return l_pos, 2 return l_pos, 1 # LOWER l_neg = [l[0], l[1], l[0]+1, l[1]+1] l_pos = [l[0]+1, l[1], l[2], l[3]] l_v = [l[0], l[1], l[0]+1, l[1]] r_v = [l[2], l[3], l[2]+1, l[3]] # negative slope exist if l_neg in lines and not line_overlaps(r_v, lines): if self.incomplete_exist(r_v, lines, "ver_right"): return r_v, 2 return r_v, 1 # positive slope exist elif l_pos in lines and not line_overlaps(l_v, lines): if self.incomplete_exist(l_v, lines, "ver_left"): return l_v, 2 return l_v, 1 # left vertical line exist elif l_v in lines and not line_overlaps(l_pos, lines): if self.incomplete_exist(l_pos, lines, "pos_bot"): return l_pos, 2 return l_pos, 1 # right vertical line exist elif r_v in lines and not line_overlaps(l_neg, lines): if self.incomplete_exist(l_neg, lines, "neg_bot"): return l_neg, 2 return l_neg, 1 # if line is vertical elif l[1] == l[3]: # LEFT l_neg = [l[0], l[1]-1, l[2], l[3]] l_pos = [l[2], l[3]-1, l[0], l[1]] t_h = [l[0], l[1]-1, l[0], l[1]] b_h = [l[2], l[3]-1, l[2], l[3]] # negative slope exist if l_neg in lines and not line_overlaps(t_h, lines): if self.incomplete_exist(t_h, lines, "hor_top"): return t_h, 2 return t_h, 1 # positive slope exist elif l_pos in lines and not line_overlaps(b_h, lines): if self.incomplete_exist(b_h, lines, "hor_bot"): return b_h, 2 return b_h, 1 # top horizontal line exist elif t_h in lines and not line_overlaps(l_neg, lines): if self.incomplete_exist(l_neg, lines, "neg_bot"): return l_neg, 2 return l_neg, 1 # bottom horizontal line exist elif b_h in lines and not line_overlaps(l_pos, lines): if self.incomplete_exist(l_pos, lines, "pos_top"): return l_pos, 2 return l_pos, 1 # RIGHT l_neg = [l[0], l[1], l[2], l[3]+1] l_pos = [l[2], l[3], l[0], l[1]+1] t_h = [l[0], l[1], l[0], l[1]+1] b_h = [l[2], l[3], l[2], l[3]+1] # negative slope exist if l_neg in lines and not line_overlaps(b_h, lines): if self.incomplete_exist(b_h, lines, "hor_bot"): return b_h, 2 return b_h, 1 # positive slope exist elif l_pos in lines and not line_overlaps(t_h, lines): if self.incomplete_exist(t_h, lines, "hor_top"): return t_h, 2 return t_h, 1 # top horizontal line exist elif t_h in lines and not line_overlaps(l_pos, lines): if self.incomplete_exist(l_pos, lines, "pos_bot"): return l_pos, 2 return l_pos, 1 # bottom horizontal line exist elif b_h in lines and not line_overlaps(l_neg, lines): if self.incomplete_exist(l_neg, lines, "neg_top"): return l_neg, 2 return l_neg, 1 # Note that unlike the vertical and horizontal lines, # slopes can only have 2 possible triangles # if line is a positive slope / elif l[2]-l[0] < 0 and l[3]-l[1] > 0: # TOP t_h = [l[2], l[3]-1, l[2], l[3]] l_v = [l[0]-1, l[1], l[0], l[1]] if t_h in lines and not line_overlaps(l_v, lines): if self.incomplete_exist(l_v, lines, "ver_left"): return l_v, 2 return l_v, 1 elif l_v in lines and\ not line_overlaps(t_h, lines): if self.incomplete_exist(t_h, lines, "hor_top"): return t_h, 2 return t_h, 1 # BOTTOM b_h = [l[0], l[1], l[0], l[1]+1] r_v = [l[2], l[3], l[2]+1, l[3]] if b_h in lines and not line_overlaps(r_v, lines): if self.incomplete_exist(r_v, lines, "ver_right"): return r_v, 2 return r_v, 1 elif r_v in lines and\ not line_overlaps(b_h, lines): if self.incomplete_exist(b_h, lines, "hor_bot"): return b_h, 2 return b_h, 1 # if line is a negative slope \ elif l[2]-l[0] > 0 and l[3]-l[1] > 0: # TOP t_h = [l[0], l[1], l[0], l[1]+1] r_v = [l[2]-1, l[3], l[2], l[3]] if t_h in lines and not line_overlaps(r_v, lines): if self.incomplete_exist(r_v, lines, "ver_right"): return r_v, 2 return r_v, 1 elif r_v in lines and\ not line_overlaps(t_h, lines): if self.incomplete_exist(t_h, lines, "hor_top"): return t_h, 2 return t_h, 1 # BOTTOM b_h = [l[2], l[3]-1, l[2], l[3]] l_v = [l[0], l[1], l[0]+1, l[1]] if b_h in lines and not line_overlaps(l_v, lines): if self.incomplete_exist(l_v, lines, "ver_left"): return l_v, 2 return l_v, 1 elif l_v in lines and\ not line_overlaps(b_h, lines): if self.incomplete_exist(b_h, lines, "hor_bot"): return b_h, 2 return b_h, 1
def drawLine(self, lines): """ This is called when it's this player's turn to draw a line. This is the method to implement by the student. This method provides the lines that are currently drawn in the game board. Each line in lines is a list of ints in this format: [m1, n1, m2, n2] The above cartesian coordinates correspond to the game board: The gameboard is an imaginary object that has the following layout: --------------------------> m | (0,0) (0,1) (0,2) (0,3) | (1,0) (1,1) (1,2) (1,3) | (2,0) (2,1) (2,2) (2,3) | (3,0) (3,1) (3,2) (3,3) n (grows downwards) e.g. lines = [ [0,0, 0,1], [1,0, 0,1], [0,0, 1,0], ] this contains 3 lines that forms a triangle * Note that lines can be an empty list [] * There are 42 possible lines, 33 of which will be used in a single game (discounting the overlapping lines) all_possible_valid_lines = [ # all horizontal lines [0,0, 0,1], [0,1, 0,2], [0,2, 0,3], [1,0, 1,1], [1,1, 1,2], [1,2, 1,3], [2,0, 2,1], [2,1, 2,2], [2,2, 2,3], [3,0, 3,1], [3,1, 3,2], [3,2, 3,3], # all vertical lines [0,0, 1,0], [0,1, 1,1], [0,2, 1,2], [0,3, 1,3], [1,0, 2,0], [1,1, 2,1], [1,2, 2,2], [1,3, 2,3], [2,0, 3,0], [2,1, 3,1], [2,2, 3,2], [2,3, 3,3], # all positive slopes [1,0, 0,1], [1,1, 0,2], [1,2, 0,3], [2,0, 1,1], [2,1, 1,2], [2,2, 1,3], [3,0, 2,1], [3,1, 2,2], [3,2, 2,3], # all negative slopes [0,0, 1,1], [0,1, 1,2], [0,2, 1,3], [1,0, 2,1], [1,1, 2,2], [1,2, 2,3], [2,0, 3,1], [2,1, 3,2], [2,2, 3,3], ] ** This method needs to return a valid, non-overlapping line or you lose. So, you should definitely check the validity of the line first before returning it to avoid disqualification. Do this by: if not line_overlaps(my_line, lines) and\ line_is_valid(my_line): # fix it. It should never go here if you know what you # are doing. return my_line 1) Use line_is_valid(line) to check if the line follows the convention below. As a convention of the game, lines should be written from left to right and from up to down e.g. VALID vertical lines: '0010', '1121', ... horizontal lines: '0001', '1112', ... positive slope: '1001', '1203', ... negative slope: '0011', '1223', ... INVALID vertical lines: '1000', '2111', ... horizontal lines: '0100', '1211', ... positive slope: '0110', '0312', ... negative slope: '1100', '2312', ... 2) Use line_overlaps(line, lines) to check if line is already drawn (already in lines) and if it overlaps another line. """ all_lines = self.all_possible_valid_lines # choose a random line chosen_line_index = randint(0, len(all_lines)-1) chosen_line = all_lines[chosen_line_index] # if the line is invalid, remove it and choose another if all_lines: # not empty while line_overlaps(chosen_line, lines) or\ not line_is_valid(chosen_line): all_lines.remove(chosen_line) chosen_line_index = randint(0, len(all_lines)-1) chosen_line = all_lines[chosen_line_index] return chosen_line
def drawLine(self, lines): """ Strategy ----------------------------------------------------- Step 1: check for incomplete triangles in lines - if there is such a triangle, then complete it. Step 2: if step 1 does not return a line, choose a line that will not leave in an incomplete triangle for the other player after this turn. Step 3: if step 2 does not return a line, choose a line that will result in an incomplete triangle but will result in the least amount of triangles being completed by the other player in the following turn. Notes -------------------------------------------------------- The gameboard is an imaginary object that has the following layout: --------------------------> m | (0,0) (0,1) (0,2) (0,3) | (1,0) (1,1) (1,2) (1,3) | (2,0) (2,1) (2,2) (2,3) | (3,0) (3,1) (3,2) (3,3) n (grows downwards) """ # first turn of the game, choose arbitrary line. if not lines: return [0, 0, 0, 1] # update the list of lines that are not yet drawn lines_to_rmv = [] for line in self.all_possible_valid_lines: if line_overlaps(line, lines) or\ not line_is_valid(line): lines_to_rmv.append(line) for l in lines_to_rmv: self.all_possible_valid_lines.remove(l) # if step 1 fails, do not let opponent predict what # we will draw shuffle(self.all_possible_valid_lines) # Step 1 c = self.line_complete(lines) if c: return c[0] # Step 2 for line in self.all_possible_valid_lines: tmp = lines[:] tmp.append(line) c = self.line_complete(tmp) if not c: return line # Step 3 min_score, l = 9001, None for line in self.all_possible_valid_lines: score = 0 tmp = lines[:] tmp.append(line) while True: c = self.line_complete(tmp) if c: score += c[1] tmp.append(c[0]) continue break if score < min_score or not l: min_score = score l = line return l