From fc4d9477d625e49a93ca29cfd5e7c4cce6a069c2 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Tue, 25 Sep 2018 16:17:10 +0200 Subject: [PATCH 01/47] Add .idea folder to gitignore. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index ff7f8d0..570f2cf 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ #Compiled python *.pyc + +#Pycharm idea folder +.idea/ From b5ee10a570edfea7c6e81812fdfd83dc5360a405 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Tue, 25 Sep 2018 16:23:32 +0200 Subject: [PATCH 02/47] Changed all raw_input to input. --- exercises/adventure/adventure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/adventure/adventure.py b/exercises/adventure/adventure.py index f813a63..7cb2cd8 100644 --- a/exercises/adventure/adventure.py +++ b/exercises/adventure/adventure.py @@ -39,7 +39,7 @@ def perform_action(input): def await_action(): - input = raw_input("> ").split() + input = input("> ").split() for case in switch(input[0]): if case('quit'): From e99bea5d16377176b20c14c0d9b87d484a856dc4 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Tue, 25 Sep 2018 16:27:40 +0200 Subject: [PATCH 03/47] Add parenthesis to all prints. --- exercises/adventure/adventure.py | 14 +++++++------- exercises/adventure/location.py | 14 +++++++------- exercises/adventure/locations.py | 20 ++++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/exercises/adventure/adventure.py b/exercises/adventure/adventure.py index 7cb2cd8..aadfc45 100644 --- a/exercises/adventure/adventure.py +++ b/exercises/adventure/adventure.py @@ -14,7 +14,7 @@ def move(direction): current_location = new_loc here = locations[current_location] else: - print "No route leading " + direction + ".\n" + print ("No route leading " + direction + ".\n") look() @@ -25,11 +25,11 @@ def look(direction=""): view = locations[target].short_view() else: view = "There's nothing there." - print view + print (view) else: - print here.name - print here.long_view() - print "" + print (here.name) + print (here.long_view()) + print ("") here.print_exits() @@ -43,14 +43,14 @@ def await_action(): for case in switch(input[0]): if case('quit'): - print "Bye!" + print ("Bye!") return if case('go','move'): if len(input) > 1: move(input[1]) else: - print "Where to?" + print ("Where to?") break if case('look'): diff --git a/exercises/adventure/location.py b/exercises/adventure/location.py index bce1c0b..60966a8 100644 --- a/exercises/adventure/location.py +++ b/exercises/adventure/location.py @@ -47,17 +47,17 @@ def print_exits(self): for case in switch(len(possible_dirs)): if case(0): - print "All dressed up and nowhere to go!" + print ("All dressed up and nowhere to go!") break if case(1): - print "There's an exit " + possible_dirs[0] + print ("There's an exit " + possible_dirs[0]) break else: - print "There are exits ", - print possible_dirs[0], + print ("There are exits "), + print (possible_dirs[0]), for ex in possible_dirs[1:]: - print ", " + ex, - print "." + print (", " + ex), + print (".") @@ -66,7 +66,7 @@ def do_something(self, input): if (input[0] in possible_actions): possible_actions[input[0]](input[1:]) else: - print "Interesting." + print ("Interesting.") def set_state(self, new_state): self.current_state_ix = new_state diff --git a/exercises/adventure/locations.py b/exercises/adventure/locations.py index d43d015..982677a 100644 --- a/exercises/adventure/locations.py +++ b/exercises/adventure/locations.py @@ -10,33 +10,33 @@ def __haunted_forest_pick(input): for case in switch(input[0]): if case('mushroom', 'mushrooms'): if haunted_forest.current_state_ix == 0: - print "Mmm... Mushrooms!\n" + print ("Mmm... Mushrooms!\n") haunted_forest.set_state(1) else: - print "Where have all the mushrooms gone? Young girls picked them everyone!" + print ("Where have all the mushrooms gone? Young girls picked them everyone!") break if case('tree','trees'): - print "You're not that strong, silly!" + print ("You're not that strong, silly!") break if case('stuff'): - print "Some stuff stuffed away." + print ("Some stuff stuffed away.") break else: - print "Ain't no " + input[0] + " around here" + print ("Ain't no " + input[0] + " around here") else: - print "Pick-pickety-pick... pickaxe?" + print ("Pick-pickety-pick... pickaxe?") def __haunted_forest_sing(input): if input: for case in switch(input[0]): if case('halelujah'): - print "The world has become a brighter place!" + print ("The world has become a brighter place!") haunted_forest.set_state(2) break else: - print "*sings* " + input[1] + print ("*sings* " + input[1]) else: - print "I'm siiiiiiingin' in the rain!" + print ("I'm siiiiiiingin' in the rain!") haunted_forest = Location('The Haunted Forest', @@ -67,7 +67,7 @@ def __haunted_forest_sing(input): """ def __shining_plains_win(input): - print "Awesome! You have won the game!" + print ("Awesome! You have won the game!") sys.exit() shining_plains = Location('The Shining Plains', From 420334ce8d1ace95bcf36a205ef179d30faf4676 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Tue, 25 Sep 2018 16:34:27 +0200 Subject: [PATCH 04/47] Renamed input to user_input to avoid name conflict. --- exercises/adventure/adventure.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/exercises/adventure/adventure.py b/exercises/adventure/adventure.py index aadfc45..b972b82 100644 --- a/exercises/adventure/adventure.py +++ b/exercises/adventure/adventure.py @@ -39,29 +39,29 @@ def perform_action(input): def await_action(): - input = input("> ").split() + user_input = input("> ").split() - for case in switch(input[0]): + for case in switch(user_input[0]): if case('quit'): print ("Bye!") return if case('go','move'): - if len(input) > 1: - move(input[1]) + if len(user_input) > 1: + move(user_input[1]) else: print ("Where to?") break if case('look'): - if len(input) > 1: - look(input[1]) + if len(user_input) > 1: + look(user_input[1]) else: look() break # We have a custom action - perform_action(input) + perform_action(user_input) await_action() From e6dcb03bbfcc1764e4fc694c0f79c2317e4669b7 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Thu, 27 Sep 2018 22:23:54 +0200 Subject: [PATCH 05/47] Reformat code --- exercises/adventure/adventure.py | 26 +++++----- exercises/adventure/location.py | 31 +++++------- exercises/adventure/locations.py | 87 +++++++++++++++++--------------- exercises/adventure/switch.py | 2 +- 4 files changed, 74 insertions(+), 72 deletions(-) diff --git a/exercises/adventure/adventure.py b/exercises/adventure/adventure.py index b972b82..b78d3d5 100644 --- a/exercises/adventure/adventure.py +++ b/exercises/adventure/adventure.py @@ -1,11 +1,10 @@ -import sys from locations import * -from location import * from switch import * current_location = 'haunted forest' here = locations[current_location] + def move(direction): global here, current_location new_loc = here.get_neighbor(direction) @@ -14,10 +13,11 @@ def move(direction): current_location = new_loc here = locations[current_location] else: - print ("No route leading " + direction + ".\n") + print("No route leading " + direction + ".\n") look() + def look(direction=""): if (direction): target = here.get_neighbor(direction) @@ -25,32 +25,31 @@ def look(direction=""): view = locations[target].short_view() else: view = "There's nothing there." - print (view) + print(view) else: - print (here.name) - print (here.long_view()) - print ("") + print(here.name) + print(here.long_view()) + print("") here.print_exits() - + def perform_action(input): here.do_something(input) - def await_action(): user_input = input("> ").split() for case in switch(user_input[0]): if case('quit'): - print ("Bye!") + print("Bye!") return - if case('go','move'): + if case('go', 'move'): if len(user_input) > 1: move(user_input[1]) else: - print ("Where to?") + print("Where to?") break if case('look'): @@ -62,9 +61,10 @@ def await_action(): # We have a custom action perform_action(user_input) - + await_action() + """ This starts the adventure game loop. """ diff --git a/exercises/adventure/location.py b/exercises/adventure/location.py index 60966a8..b761866 100644 --- a/exercises/adventure/location.py +++ b/exercises/adventure/location.py @@ -1,9 +1,9 @@ from switch import * -class LocationState: +class LocationState: short_desc = "" - long_desc = "" + long_desc = "" directions = {} @@ -11,15 +11,15 @@ class LocationState: def __init__(self, short, long, dirs, acts): self.short_desc = short - self.long_desc = long + self.long_desc = long self.directions = dirs - self.actions = acts + self.actions = acts -class Location: +class Location: name = "Alien Mothership" states = [] - current_state_ix = 0 + current_state_ix = 0 current_state = "" # Args should be a non-empty set of LocationStates @@ -41,32 +41,29 @@ def get_neighbor(self, direction): else: return "" - def print_exits(self): possible_dirs = self.current_state.directions.keys() - + for case in switch(len(possible_dirs)): if case(0): - print ("All dressed up and nowhere to go!") + print("All dressed up and nowhere to go!") break if case(1): - print ("There's an exit " + possible_dirs[0]) + print("There's an exit " + possible_dirs[0]) break else: - print ("There are exits "), - print (possible_dirs[0]), + print("There are exits "), + print(possible_dirs[0]), for ex in possible_dirs[1:]: - print (", " + ex), - print (".") - - + print(", " + ex), + print(".") def do_something(self, input): possible_actions = self.current_state.actions if (input[0] in possible_actions): possible_actions[input[0]](input[1:]) else: - print ("Interesting.") + print("Interesting.") def set_state(self, new_state): self.current_state_ix = new_state diff --git a/exercises/adventure/locations.py b/exercises/adventure/locations.py index 982677a..d6cc7c2 100644 --- a/exercises/adventure/locations.py +++ b/exercises/adventure/locations.py @@ -1,82 +1,88 @@ import sys + from location import * from switch import * """ The haunted forest """ + + def __haunted_forest_pick(input): if input: for case in switch(input[0]): if case('mushroom', 'mushrooms'): if haunted_forest.current_state_ix == 0: - print ("Mmm... Mushrooms!\n") + print("Mmm... Mushrooms!\n") haunted_forest.set_state(1) else: - print ("Where have all the mushrooms gone? Young girls picked them everyone!") + print("Where have all the mushrooms gone? Young girls picked them everyone!") break - if case('tree','trees'): - print ("You're not that strong, silly!") + if case('tree', 'trees'): + print("You're not that strong, silly!") break if case('stuff'): - print ("Some stuff stuffed away.") + print("Some stuff stuffed away.") break else: - print ("Ain't no " + input[0] + " around here") + print("Ain't no " + input[0] + " around here") else: - print ("Pick-pickety-pick... pickaxe?") + print("Pick-pickety-pick... pickaxe?") + def __haunted_forest_sing(input): if input: for case in switch(input[0]): if case('halelujah'): - print ("The world has become a brighter place!") + print("The world has become a brighter place!") haunted_forest.set_state(2) break else: - print ("*sings* " + input[1]) + print("*sings* " + input[1]) else: - print ("I'm siiiiiiingin' in the rain!") - + print("I'm siiiiiiingin' in the rain!") -haunted_forest = Location('The Haunted Forest', - LocationState( - "A dark and dreary place.", - "A scary forest full of trees and mushrooms and stuff.", - {}, - {'pick': __haunted_forest_pick} - ), - LocationState( - "A dark and dreary place.", - "A scary forest full of trees but no mushrooms.", - {}, - {'pick': __haunted_forest_pick, - 'sing': __haunted_forest_sing} - ), - LocationState( - "A dark and dreary place.", - "A scary forest full of trees but no mushrooms.", - {'west':'shining plains'}, - {'pick': __haunted_forest_pick, - 'sing': __haunted_forest_sing} - ) -) + +haunted_forest = Location('The Haunted Forest', + LocationState( + "A dark and dreary place.", + "A scary forest full of trees and mushrooms and stuff.", + {}, + {'pick': __haunted_forest_pick} + ), + LocationState( + "A dark and dreary place.", + "A scary forest full of trees but no mushrooms.", + {}, + {'pick': __haunted_forest_pick, + 'sing': __haunted_forest_sing} + ), + LocationState( + "A dark and dreary place.", + "A scary forest full of trees but no mushrooms.", + {'west': 'shining plains'}, + {'pick': __haunted_forest_pick, + 'sing': __haunted_forest_sing} + ) + ) """ The Shining Plains. """ + def __shining_plains_win(input): - print ("Awesome! You have won the game!") + print("Awesome! You have won the game!") sys.exit() + shining_plains = Location('The Shining Plains', - LocationState( - "A bright and beautiful place.", - "A really bright and beautiful plains with daffodils and dandelions.", - {'east':'haunted forest'}, - {'win': __shining_plains_win} - )) + LocationState( + "A bright and beautiful place.", + "A really bright and beautiful plains with daffodils and dandelions.", + {'east': 'haunted forest'}, + {'win': __shining_plains_win} + )) #####################################################3 @@ -85,4 +91,3 @@ def __shining_plains_win(input): 'haunted forest': haunted_forest, 'shining plains': shining_plains } - diff --git a/exercises/adventure/switch.py b/exercises/adventure/switch.py index c8ae158..9016a37 100644 --- a/exercises/adventure/switch.py +++ b/exercises/adventure/switch.py @@ -7,7 +7,7 @@ def __iter__(self): """Return the match method once, then stop""" yield self.match raise StopIteration - + def match(self, *args): """Indicate whether or not to enter a case suite""" if self.fall or not args: From c1fad5987c23e2d68dba735b5eb58368ed6f7e5a Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 29 Sep 2018 14:25:03 +0200 Subject: [PATCH 06/47] Fixed code format for excercise. --- exercises/battleship/battleship.py | 858 +++++++++--------- exercises/battleship/example/battleship.py | 766 ++++++++-------- exercises/battleship/example/ct_pygame.py | 978 +++++++++++---------- 3 files changed, 1360 insertions(+), 1242 deletions(-) diff --git a/exercises/battleship/battleship.py b/exercises/battleship/battleship.py index 383ab6c..3843c75 100644 --- a/exercises/battleship/battleship.py +++ b/exercises/battleship/battleship.py @@ -1,441 +1,463 @@ - import curses import time + ## ## An implementation of the game Battleship ## def task_initScreen(): - screen = curses.initscr() - curses.noecho() - curses.cbreak() - screen.keypad( 1 ) - curses.curs_set( 0 ) - screen.move( 0, 0 ) - screen.addstr( ">" ) - screen.move( 1, 0 ) - screen.addstr( "#" ) - task_drawBoard( screen ) - return screen - -def task_terminateScreen( screen ): - curses.curs_set( 1 ) - curses.nocbreak() - screen.keypad( 0 ) - curses.echo() - curses.endwin() - return + screen = curses.initscr() + curses.noecho() + curses.cbreak() + screen.keypad(1) + curses.curs_set(0) + screen.move(0, 0) + screen.addstr(">") + screen.move(1, 0) + screen.addstr("#") + task_drawBoard(screen) + return screen + + +def task_terminateScreen(screen): + curses.curs_set(1) + curses.nocbreak() + screen.keypad(0) + curses.echo() + curses.endwin() + return + class Ship: - def __init__( self ): - self.points = [] - return + def __init__(self): + self.points = [] + return + class Player: - def __init__( self ): - self.ships = [] - self.shots = [] - return + def __init__(self): + self.ships = [] + self.shots = [] + return + class Environment: - def __init__( self ): - self.resetCursor() - self.players = [ Player(), Player() ] - self.current = None - self.opponent = None - self.rotate = False - self.ship_placement = None - self.current_offset_x = 0 - self.opponent_offset_x = 11 - self.ship_stack = None - - #mode -1 - initial mode - #mode 0 - setup player on the right side - #mode 1 - setup player on the left side - #mode 2 - right side player attack - #mode 3 - left side player attack - - self.mode = -1 - return - - def resetCursor( self ): - self.cursor = ( 4, 4 ) - self.rotate = False - return - - def currentCursorOffset( self ): - if self.mode == 0: - return 0 - elif self.mode == 1: - return 11 - else: - return self.opponent_offset_x - - def getOrientationString( self ): - if self.rotate: - return "Vertical" - else: - return "Horizontal" - - def turn( self ): - self.rotate = not self.rotate - return - - def cursorCoversCurrentShips( self ): - return self.coversCurrentShips( self.cursor ) - - def cursorCoversOpponentShips( self ): - return self.coversOpponentShips( self.cursor ) - - def coversCurrentShips( self, coords ): - return self.coversShips( coords, self.current ) - - def coversOpponentShips( self, coords ): - return self.coversShips( coords, self.opponent ) - - def coversShips( self, coords, player ): - for ship in player.ships: - for point in ship.points: - if point[ 0 ] == coords[ 0 ] and point[ 1 ] == coords[ 1 ]: - return True - else: - pass - return not task_validCoord( coords ) - - def advance( self ): - self.mode = self.mode + 1 - if self.mode > 3: - self.mode = 2 - else: - pass - self.resetCursor() - if self.mode % 2 == 0: - self.current = self.players[ 0 ] - self.opponent = self.players[ 1 ] - self.current_offset_x = 0 - self.opponent_offset_x = 11 - else: - self.current = self.players[ 1 ] - self.opponent = self.players[ 0 ] - self.current_offset_x = 11 - self.opponent_offset_x = 0 - if self.mode == 0: - self.ship_stack = task_getShipStack() - return "Player 1 - place your ships!" - elif self.mode == 1: - self.ship_stack = task_getShipStack() - return "Player 2 - place your ships!" - elif self.mode == 2: - return "Player 1 - attack your opponents ships!" - else: - return "Player 2 - attack your opponents ships!" - - def allowAdvance( self ): - return len( self.ship_stack ) == 0 - - def turnShipPoint( self, point ): - if self.rotate: - return ( point[ 1 ], point[ 0 ], point[ 2 ] ) - else: - return point - - def allowPlacement( self ): - index = len( self.ship_stack ) - if index == 0: - return False - else: - ship = self.ship_stack[ index - 1 ] - for point in ship.points: - s_point = self.turnShipPoint( point ) - n_point = ( self.cursor[ 0 ] + s_point[ 0 ], self.cursor[ 1 ] + s_point[ 1 ], s_point[ 2 ] ) - if self.coversCurrentShips( n_point ): - return False - else: - pass - return True - - def place( self ): - source_ship = self.ship_stack.pop() - new_ship = Ship() - for point in source_ship.points: - s_point = self.turnShipPoint( point ) - n_point = ( self.cursor[ 0 ] + s_point[ 0 ], self.cursor[ 1 ] + s_point[ 1 ], s_point[ 2 ] ) - new_ship.points.append( n_point ) - self.current.ships.append( new_ship ) - - def shoot( self ): - self.current.shots.append( self.cursor ) - for ship in self.opponent.ships: - i = 0 - for point in ship.points: - if point[ 0 ] == self.cursor[ 0 ] and point[ 1 ] == self.cursor[ 1 ]: - ship.points[ i ] = ( point[ 0 ], point[ 1 ], True ) - return True - else: - pass - i = i + 1 - return False - - def opponentDefeated( self ): - for ship in self.opponent.ships: - for point in ship.points: - if point[ 2 ]: - pass - else: - return False - return True - + def __init__(self): + self.resetCursor() + self.players = [Player(), Player()] + self.current = None + self.opponent = None + self.rotate = False + self.ship_placement = None + self.current_offset_x = 0 + self.opponent_offset_x = 11 + self.ship_stack = None + + # mode -1 - initial mode + # mode 0 - setup player on the right side + # mode 1 - setup player on the left side + # mode 2 - right side player attack + # mode 3 - left side player attack + + self.mode = -1 + return + + def resetCursor(self): + self.cursor = (4, 4) + self.rotate = False + return + + def currentCursorOffset(self): + if self.mode == 0: + return 0 + elif self.mode == 1: + return 11 + else: + return self.opponent_offset_x + + def getOrientationString(self): + if self.rotate: + return "Vertical" + else: + return "Horizontal" + + def turn(self): + self.rotate = not self.rotate + return + + def cursorCoversCurrentShips(self): + return self.coversCurrentShips(self.cursor) + + def cursorCoversOpponentShips(self): + return self.coversOpponentShips(self.cursor) + + def coversCurrentShips(self, coords): + return self.coversShips(coords, self.current) + + def coversOpponentShips(self, coords): + return self.coversShips(coords, self.opponent) + + def coversShips(self, coords, player): + for ship in player.ships: + for point in ship.points: + if point[0] == coords[0] and point[1] == coords[1]: + return True + else: + pass + return not task_validCoord(coords) + + def advance(self): + self.mode = self.mode + 1 + if self.mode > 3: + self.mode = 2 + else: + pass + self.resetCursor() + if self.mode % 2 == 0: + self.current = self.players[0] + self.opponent = self.players[1] + self.current_offset_x = 0 + self.opponent_offset_x = 11 + else: + self.current = self.players[1] + self.opponent = self.players[0] + self.current_offset_x = 11 + self.opponent_offset_x = 0 + if self.mode == 0: + self.ship_stack = task_getShipStack() + return "Player 1 - place your ships!" + elif self.mode == 1: + self.ship_stack = task_getShipStack() + return "Player 2 - place your ships!" + elif self.mode == 2: + return "Player 1 - attack your opponents ships!" + else: + return "Player 2 - attack your opponents ships!" + + def allowAdvance(self): + return len(self.ship_stack) == 0 + + def turnShipPoint(self, point): + if self.rotate: + return (point[1], point[0], point[2]) + else: + return point + + def allowPlacement(self): + index = len(self.ship_stack) + if index == 0: + return False + else: + ship = self.ship_stack[index - 1] + for point in ship.points: + s_point = self.turnShipPoint(point) + n_point = (self.cursor[0] + s_point[0], self.cursor[1] + s_point[1], s_point[2]) + if self.coversCurrentShips(n_point): + return False + else: + pass + return True + + def place(self): + source_ship = self.ship_stack.pop() + new_ship = Ship() + for point in source_ship.points: + s_point = self.turnShipPoint(point) + n_point = (self.cursor[0] + s_point[0], self.cursor[1] + s_point[1], s_point[2]) + new_ship.points.append(n_point) + self.current.ships.append(new_ship) + + def shoot(self): + self.current.shots.append(self.cursor) + for ship in self.opponent.ships: + i = 0 + for point in ship.points: + if point[0] == self.cursor[0] and point[1] == self.cursor[1]: + ship.points[i] = (point[0], point[1], True) + return True + else: + pass + i = i + 1 + return False + + def opponentDefeated(self): + for ship in self.opponent.ships: + for point in ship.points: + if point[2]: + pass + else: + return False + return True + + def task_makeLargeShip(): - ship = Ship() - ship.points.append( ( 0, 0, False ) ) - ship.points.append( ( 1, 0, False ) ) - ship.points.append( ( 2, 0, False ) ) - return ship - + ship = Ship() + ship.points.append((0, 0, False)) + ship.points.append((1, 0, False)) + ship.points.append((2, 0, False)) + return ship + + def task_makeSquareShip(): - ship = Ship() - ship.points.append( ( 0, 0, False ) ) - ship.points.append( ( 1, 0, False ) ) - ship.points.append( ( 1, 1, False ) ) - ship.points.append( ( 0, 1, False ) ) - return ship - + ship = Ship() + ship.points.append((0, 0, False)) + ship.points.append((1, 0, False)) + ship.points.append((1, 1, False)) + ship.points.append((0, 1, False)) + return ship + + def task_makeSmallShip(): - ship = Ship() - ship.points.append( ( 0, 0, False ) ) - return ship + ship = Ship() + ship.points.append((0, 0, False)) + return ship + def task_getShipStack(): - return [ task_makeSmallShip(), task_makeSmallShip(), task_makeSquareShip(), task_makeLargeShip() ] - -def task_mainLoop( screen ): - environment = Environment() - task_outputSplash( screen, environment.advance() ) - while( True ): - task_clearPlayer( screen, environment.current_offset_x ) - task_drawPlayer( screen, environment.current, environment.current_offset_x ) - task_hidePlayer( screen, environment.opponent_offset_x ) - task_drawPlayerShots( screen, environment ) - task_drawCursor( screen, environment, environment.opponent, environment.currentCursorOffset() ) - screen.refresh() - command = task_getCommand( screen ) - cmd = command[ 0 ] - coords = command[ 1 ] - if cmd == "exit": - if coords is None: - task_outputSplash( screen, "Exiting program" ) - else: - task_outputSplash( screen, "'exit' does not take any arguments" ) - break - elif cmd == "turn": - if coords is None: - if environment.mode > 1: - task_outputSplash( screen, "Turning of ships is not allowed in 'battle' mode!" ) - else: - environment.turn() - task_outputSplash( screen, "Orientation: " + environment.getOrientationString() ) - else: - task_outputSplash( screen, "'turn' does not take any arguments" ) - elif cmd == "done": - if coords is None: - if environment.mode > 1: - task_outputSplash( screen, "Turn skipping is not allowed in 'battle' mode!" ) - elif environment.allowAdvance(): - task_outputSplash( screen, environment.advance() ) - else: - task_outputSplash( screen, "You have " + str( len( environment.ship_stack ) ) + " more ships to place." ) - else: - task_outputSplash( screen, "'done' does not take any arguments" ) - elif cmd == "place": - if coords is None: - if environment.mode > 1: - task_outputSplash( screen, "Placement of ships is not allowed in 'battle' mode!" ) - elif environment.allowPlacement(): - environment.place() - task_outputSplash( screen, "Placed ship at " + task_coordinateToString( environment.cursor ) ) - else: - task_outputSplash( screen, "Unable to place ship at coordinates!" ) - else: - task_outputSplash( screen, "'place' does not take any arguments" ) - elif cmd == "move": - str_coords = task_coordinateToString( coords ) - if task_validCoord( coords ): - task_outputSplash( screen, "Moving to " + str_coords ) - environment.cursor = coords - else: - task_outputSplash( screen, "Invalid coordinates" + str_coords ) - elif cmd == "shoot": - if coords is None: - if environment.mode < 2: - task_outputSplash( screen, "Shooting of ships is not allowed in 'prepare' mode!" ) - else: - task_outputSplash( screen, "Shooting at " + task_coordinateToString( environment.cursor ) ) - hit = environment.shoot() - task_drawPlayerShots( screen, environment ) - if hit: - task_outputSplash( screen, "And it is a hit!" ) - if environment.opponentDefeated(): - if environment.current is environment.players[ 0 ]: - task_outputSplash( screen, "Player 1 wins!" ) - else: - task_outputSplash( screen, "Player 2 wins!" ) - break - else: - pass - else: - task_outputSplash( screen, "...but missed!" ) - environment.advance() - else: - task_outputSplash( screen, "'shoot' does not take any arguments" ) - else: - task_outputSplash( screen, "Unknown command: " + cmd ) - task_removeCursor( screen, environment, environment.currentCursorOffset() ) - return - -def task_drawPlayer( screen, player, offset_x ): - for ship in player.ships: - for point in ship.points: - if point[ 2 ]: - symbol = "☠" - else: - symbol = "☸" - screen.move( 3 + point[ 1 ], 1 + offset_x + point[ 0 ] ) - screen.addstr( symbol ) - return - -def task_hidePlayer( screen, offset_x ): - for y in range( 0, 10 ): - screen.move( 3 + y, 1 + offset_x ) - screen.addstr( "----------" ) - return - -def task_clearPlayer( screen, offset_x ): - for y in range( 0, 10 ): - screen.move( 3 + y, 1 + offset_x ) - screen.addstr( " " ) - return - -def task_drawPlayerShots( screen, environment ): - for shot in environment.current.shots: - screen.move( 3 + shot[ 1 ], 1 + shot[ 0 ] + environment.opponent_offset_x ) - if environment.coversOpponentShips( shot ): - screen.addstr( "☠" ) - else: - screen.addstr( "♨" ) - -def task_removeCursor( screen, environment, offset_x ): - screen.move( environment.cursor[ 1 ] + 3, environment.cursor[ 0 ] + 1 + offset_x ) - screen.addstr( " " ) - return - -def task_drawCursor( screen, environment, player, offset_x ): - screen.move( environment.cursor[ 1 ] + 3, environment.cursor[ 0 ] + 1 + offset_x ) - if environment.mode >= 2: - screen.addstr( "☢" ) - elif environment.cursorCoversCurrentShips(): - screen.addstr( "★" ) - else: - screen.addstr( "☆" ) - return - -def task_outputSplash( screen, msg ): - yx = screen.getyx() - screen.move( 1, 1 ) - screen.addstr( msg ) - screen.refresh() - time.sleep( 1 ) - screen.move( 1, 1 ) - screen.clrtoeol() - screen.refresh() - screen.move( yx[ 0 ], yx[ 1 ] ) - return - -def task_getCommand( screen ): - curses.curs_set(1) - screen.move( 0, 1 ) - instr = "" - final = None - screen.addstr( "Enter command: " ) - while( True ): - inchr = screen.getkey() - if inchr == '\n': - command = instr - coord_x = 0 - coord_y = 0 - index = instr.find( ',' ) - if index >= 0: - command = instr[ 0 : index ] - index_x = instr.find( ',', index + 1 ) - if index_x >= 0: - try: - coord_x = int( instr[ index + 1 : index_x ] ) - coord_y = int( instr[ index_x + 1 : len( instr ) ] ) - final = ( command, ( coord_x, coord_y ) ) - break - except ValueError: - pass - else: - pass - else: - final = ( command, None ) - break - screen.move( 0, 16 ) - screen.clrtoeol() - screen.addstr( "Invalid command." ) - screen.refresh() - time.sleep( 1 ) - screen.move( 0, 16 ) - screen.clrtoeol() - screen.refresh() - instr = "" - else: - instr += inchr - screen.addstr( inchr ) - screen.refresh() - screen.move( 0, 1 ) - screen.clrtoeol() - screen.refresh() - curses.curs_set(0) - return final - -def task_drawBoard( screen ): - for y in range( 0, 10 ): - symbol = str( y ) - screen.move( 3 + y, 0 ) - screen.clrtoeol() - screen.addstr( symbol ) - screen.move( 3 + y, 22 ) - screen.addstr( symbol ) - screen.move( 3 + y, 11 ) - screen.addstr( "|" ) - top_text = "0123456789" - screen.move( 2, 1 ) - screen.addstr( top_text ) - screen.move( 2, 12 ) - screen.addstr( top_text ) - screen.move( 13, 1 ) - screen.addstr( top_text ) - screen.move( 13, 12 ) - screen.addstr( top_text ) - return - -def task_validCoord( coords ): - if coords is None: - return False - else: - return coords[ 0 ] >= 0 and coords[ 0 ] < 10 and coords[ 1 ] >= 0 and coords[ 1 ] < 10 - -def task_coordinateToString( coords ): - if coords is None: - return "Missing coordinates in format x,y" - else: - return "(" + str( coords[ 0 ] ) + ", " + str( coords[ 1 ] ) + ")" + return [task_makeSmallShip(), task_makeSmallShip(), task_makeSquareShip(), task_makeLargeShip()] + + +def task_mainLoop(screen): + environment = Environment() + task_outputSplash(screen, environment.advance()) + while (True): + task_clearPlayer(screen, environment.current_offset_x) + task_drawPlayer(screen, environment.current, environment.current_offset_x) + task_hidePlayer(screen, environment.opponent_offset_x) + task_drawPlayerShots(screen, environment) + task_drawCursor(screen, environment, environment.opponent, environment.currentCursorOffset()) + screen.refresh() + command = task_getCommand(screen) + cmd = command[0] + coords = command[1] + if cmd == "exit": + if coords is None: + task_outputSplash(screen, "Exiting program") + else: + task_outputSplash(screen, "'exit' does not take any arguments") + break + elif cmd == "turn": + if coords is None: + if environment.mode > 1: + task_outputSplash(screen, "Turning of ships is not allowed in 'battle' mode!") + else: + environment.turn() + task_outputSplash(screen, "Orientation: " + environment.getOrientationString()) + else: + task_outputSplash(screen, "'turn' does not take any arguments") + elif cmd == "done": + if coords is None: + if environment.mode > 1: + task_outputSplash(screen, "Turn skipping is not allowed in 'battle' mode!") + elif environment.allowAdvance(): + task_outputSplash(screen, environment.advance()) + else: + task_outputSplash(screen, "You have " + str(len(environment.ship_stack)) + " more ships to place.") + else: + task_outputSplash(screen, "'done' does not take any arguments") + elif cmd == "place": + if coords is None: + if environment.mode > 1: + task_outputSplash(screen, "Placement of ships is not allowed in 'battle' mode!") + elif environment.allowPlacement(): + environment.place() + task_outputSplash(screen, "Placed ship at " + task_coordinateToString(environment.cursor)) + else: + task_outputSplash(screen, "Unable to place ship at coordinates!") + else: + task_outputSplash(screen, "'place' does not take any arguments") + elif cmd == "move": + str_coords = task_coordinateToString(coords) + if task_validCoord(coords): + task_outputSplash(screen, "Moving to " + str_coords) + environment.cursor = coords + else: + task_outputSplash(screen, "Invalid coordinates" + str_coords) + elif cmd == "shoot": + if coords is None: + if environment.mode < 2: + task_outputSplash(screen, "Shooting of ships is not allowed in 'prepare' mode!") + else: + task_outputSplash(screen, "Shooting at " + task_coordinateToString(environment.cursor)) + hit = environment.shoot() + task_drawPlayerShots(screen, environment) + if hit: + task_outputSplash(screen, "And it is a hit!") + if environment.opponentDefeated(): + if environment.current is environment.players[0]: + task_outputSplash(screen, "Player 1 wins!") + else: + task_outputSplash(screen, "Player 2 wins!") + break + else: + pass + else: + task_outputSplash(screen, "...but missed!") + environment.advance() + else: + task_outputSplash(screen, "'shoot' does not take any arguments") + else: + task_outputSplash(screen, "Unknown command: " + cmd) + task_removeCursor(screen, environment, environment.currentCursorOffset()) + return + + +def task_drawPlayer(screen, player, offset_x): + for ship in player.ships: + for point in ship.points: + if point[2]: + symbol = "☠" + else: + symbol = "☸" + screen.move(3 + point[1], 1 + offset_x + point[0]) + screen.addstr(symbol) + return + + +def task_hidePlayer(screen, offset_x): + for y in range(0, 10): + screen.move(3 + y, 1 + offset_x) + screen.addstr("----------") + return + + +def task_clearPlayer(screen, offset_x): + for y in range(0, 10): + screen.move(3 + y, 1 + offset_x) + screen.addstr(" ") + return + + +def task_drawPlayerShots(screen, environment): + for shot in environment.current.shots: + screen.move(3 + shot[1], 1 + shot[0] + environment.opponent_offset_x) + if environment.coversOpponentShips(shot): + screen.addstr("☠") + else: + screen.addstr("♨") + + +def task_removeCursor(screen, environment, offset_x): + screen.move(environment.cursor[1] + 3, environment.cursor[0] + 1 + offset_x) + screen.addstr(" ") + return + + +def task_drawCursor(screen, environment, player, offset_x): + screen.move(environment.cursor[1] + 3, environment.cursor[0] + 1 + offset_x) + if environment.mode >= 2: + screen.addstr("☢") + elif environment.cursorCoversCurrentShips(): + screen.addstr("★") + else: + screen.addstr("☆") + return + + +def task_outputSplash(screen, msg): + yx = screen.getyx() + screen.move(1, 1) + screen.addstr(msg) + screen.refresh() + time.sleep(1) + screen.move(1, 1) + screen.clrtoeol() + screen.refresh() + screen.move(yx[0], yx[1]) + return + + +def task_getCommand(screen): + curses.curs_set(1) + screen.move(0, 1) + instr = "" + final = None + screen.addstr("Enter command: ") + while (True): + inchr = screen.getkey() + if inchr == '\n': + command = instr + coord_x = 0 + coord_y = 0 + index = instr.find(',') + if index >= 0: + command = instr[0: index] + index_x = instr.find(',', index + 1) + if index_x >= 0: + try: + coord_x = int(instr[index + 1: index_x]) + coord_y = int(instr[index_x + 1: len(instr)]) + final = (command, (coord_x, coord_y)) + break + except ValueError: + pass + else: + pass + else: + final = (command, None) + break + screen.move(0, 16) + screen.clrtoeol() + screen.addstr("Invalid command.") + screen.refresh() + time.sleep(1) + screen.move(0, 16) + screen.clrtoeol() + screen.refresh() + instr = "" + else: + instr += inchr + screen.addstr(inchr) + screen.refresh() + screen.move(0, 1) + screen.clrtoeol() + screen.refresh() + curses.curs_set(0) + return final + + +def task_drawBoard(screen): + for y in range(0, 10): + symbol = str(y) + screen.move(3 + y, 0) + screen.clrtoeol() + screen.addstr(symbol) + screen.move(3 + y, 22) + screen.addstr(symbol) + screen.move(3 + y, 11) + screen.addstr("|") + top_text = "0123456789" + screen.move(2, 1) + screen.addstr(top_text) + screen.move(2, 12) + screen.addstr(top_text) + screen.move(13, 1) + screen.addstr(top_text) + screen.move(13, 12) + screen.addstr(top_text) + return + + +def task_validCoord(coords): + if coords is None: + return False + else: + return coords[0] >= 0 and coords[0] < 10 and coords[1] >= 0 and coords[1] < 10 + + +def task_coordinateToString(coords): + if coords is None: + return "Missing coordinates in format x,y" + else: + return "(" + str(coords[0]) + ", " + str(coords[1]) + ")" + def task_program(): - screen = task_initScreen() - task_mainLoop( screen ) - task_terminateScreen( screen ) - return + screen = task_initScreen() + task_mainLoop(screen) + task_terminateScreen(screen) + return + task_program() diff --git a/exercises/battleship/example/battleship.py b/exercises/battleship/example/battleship.py index 2946762..60a7548 100644 --- a/exercises/battleship/example/battleship.py +++ b/exercises/battleship/example/battleship.py @@ -1,7 +1,4 @@ - -import math -import random -from ct_pygame import * +from example.ct_pygame import * ## ## Global variables @@ -30,414 +27,443 @@ GAME_MODE_PLAY_PLAYER_1 = 3 GAME_MODE = GAME_MODE_PLACE_PLAYER_0 -PLAYER_BOATS = [ LARGE_BOAT, LARGE_BOAT, SMALL_BOAT, SMALL_BOAT, SMALL_BOAT ] +PLAYER_BOATS = [LARGE_BOAT, LARGE_BOAT, SMALL_BOAT, SMALL_BOAT, SMALL_BOAT] BOATS_TO_PLACE = [] CROSSES_PLACED = [] PLAYER_0_HITS = 0 PLAYER_1_HITS = 0 PLAYER_HITS_NEEDED_TO_WIN = 1 * 3 + 3 * 2 + ## ## Classes ## class Cross: - def __init__( self, position, side, did_hit ): - global ID_GENERATOR - self.position = position - self.side = side - self.tag = "marker_" + str( ID_GENERATOR ) - if did_hit: - self.image = "x.png" - else: - self.image = "miss.png" - self.did_hit = did_hit - ID_GENERATOR += 1 + def __init__(self, position, side, did_hit): + global ID_GENERATOR + self.position = position + self.side = side + self.tag = "marker_" + str(ID_GENERATOR) + if did_hit: + self.image = "x.png" + else: + self.image = "miss.png" + self.did_hit = did_hit + ID_GENERATOR += 1 + class Boat: - def __init__( self, x, y, w, h, side ): - global ID_GENERATOR - self.x = x - self.y = y - self.w = w - self.h = h - self.side = side - self.tag = "boat_" + str( ID_GENERATOR ) - ID_GENERATOR += 1 + def __init__(self, x, y, w, h, side): + global ID_GENERATOR + self.x = x + self.y = y + self.w = w + self.h = h + self.side = side + self.tag = "boat_" + str(ID_GENERATOR) + ID_GENERATOR += 1 + class Tile: - def __init__( self, x, y ): - self.x = x - self.y = y - self.tag = "tile(" + str( x ) + "," + str( y ) + ")" - self.occupant = None + def __init__(self, x, y): + self.x = x + self.y = y + self.tag = "tile(" + str(x) + "," + str(y) + ")" + self.occupant = None + ## ## ## -def coordToPosition( x, y ): - global OFF - global GAP - global SIZE - return ( OFF + x * ( SIZE + GAP ), OFF + y * ( SIZE + GAP ) ) - -def positionToCoord( x, y ): - global WID - global HEI - global OFF - global GAP - global SIZE - return ( int( round( ( x - OFF ) / ( SIZE + GAP ) ) ), - int( round( ( y - OFF ) / ( SIZE + GAP ) ) ) ) - -def getBoatImage( boat_type ): - global ORIENTATION - global HORIZONTAL - global VERTICAL - global SELECTED_BOAT_TYPE - global SMALL_BOAT - global LARGE_BOAT - if SELECTED_BOAT_TYPE == SMALL_BOAT: - return "small_boat.png" - else: - if ORIENTATION == HORIZONTAL: - return "large_boat_horizontal.png" - else: - return "large_boat_vertical.png" - -def getBoatTileBounds( boat_type ): - #Return format ( dx, dy, w, h ) - global ORIENTATION - global HORIZONTAL - global VERTICAL - global SELECTED_BOAT_TYPE - global SMALL_BOAT - global LARGE_BOAT - if SELECTED_BOAT_TYPE == SMALL_BOAT: - return ( 0, 0, 1, 1 ) - else: - if ORIENTATION == HORIZONTAL: - return ( -1, 0, 3, 1 ) - else: - return ( 0, -1, 1, 3 ) +def coordToPosition(x, y): + global OFF + global GAP + global SIZE + return (OFF + x * (SIZE + GAP), OFF + y * (SIZE + GAP)) + + +def positionToCoord(x, y): + global WID + global HEI + global OFF + global GAP + global SIZE + return (int(round((x - OFF) / (SIZE + GAP))), + int(round((y - OFF) / (SIZE + GAP)))) + + +def getBoatImage(boat_type): + global ORIENTATION + global HORIZONTAL + global VERTICAL + global SELECTED_BOAT_TYPE + global SMALL_BOAT + global LARGE_BOAT + if SELECTED_BOAT_TYPE == SMALL_BOAT: + return "small_boat.png" + else: + if ORIENTATION == HORIZONTAL: + return "large_boat_horizontal.png" + else: + return "large_boat_vertical.png" + + +def getBoatTileBounds(boat_type): + # Return format ( dx, dy, w, h ) + global ORIENTATION + global HORIZONTAL + global VERTICAL + global SELECTED_BOAT_TYPE + global SMALL_BOAT + global LARGE_BOAT + if SELECTED_BOAT_TYPE == SMALL_BOAT: + return (0, 0, 1, 1) + else: + if ORIENTATION == HORIZONTAL: + return (-1, 0, 3, 1) + else: + return (0, -1, 1, 3) + def updateCursor(): - global GAME_MODE - global GAME_MODE_PLAY_PLAYER_0 - global BOATS_TO_PLACE - global SELECTED_BOAT_TYPE - if ctHasObject( "cursor" ): - ctDestroyObject( "cursor" ) - if GAME_MODE < GAME_MODE_PLAY_PLAYER_0: - bounds = getBoatTileBounds( SELECTED_BOAT_TYPE ) - mp = ctGetMousePosition() - sz = ( SIZE * bounds[ 2 ], SIZE * bounds[ 3 ] ) - pos = ( mp[ 0 ] - sz[ 0 ] / 2.0, mp[ 1 ] - sz[ 1 ] / 2.0 ) - ctCreateObject( "cursor", getBoatImage( SELECTED_BOAT_TYPE ), - position = pos, size = sz ) - updateHeldBoat() + global GAME_MODE + global GAME_MODE_PLAY_PLAYER_0 + global BOATS_TO_PLACE + global SELECTED_BOAT_TYPE + if ctHasObject("cursor"): + ctDestroyObject("cursor") + if GAME_MODE < GAME_MODE_PLAY_PLAYER_0: + bounds = getBoatTileBounds(SELECTED_BOAT_TYPE) + mp = ctGetMousePosition() + sz = (SIZE * bounds[2], SIZE * bounds[3]) + pos = (mp[0] - sz[0] / 2.0, mp[1] - sz[1] / 2.0) + ctCreateObject("cursor", getBoatImage(SELECTED_BOAT_TYPE), + position=pos, size=sz) + updateHeldBoat() + def displayCurrentEnemies(): - global GAME_MODE - global PLACED_BOATS - global CROSSES_PLACED - side = GAME_MODE % 2 - game_over = isGameOver() - for boat in PLACED_BOATS: - ctSetObjectVisible( boat.tag, boat.side == side or game_over ) - for cross in CROSSES_PLACED: - ctSetObjectVisible( cross.tag, cross.side == side or cross.did_hit or game_over ) + global GAME_MODE + global PLACED_BOATS + global CROSSES_PLACED + side = GAME_MODE % 2 + game_over = isGameOver() + for boat in PLACED_BOATS: + ctSetObjectVisible(boat.tag, boat.side == side or game_over) + for cross in CROSSES_PLACED: + ctSetObjectVisible(cross.tag, cross.side == side or cross.did_hit or game_over) + def getEnemyXLimits(): - global WID - other_side = ( GAME_MODE + 1 ) % 2 - half = WID / 2 - min_x = other_side * half - max_x = min_x + half - return ( min_x, max_x ) - -def crossFreeTile( x, y ): - global CROSSES_PLACED - p = ( x, y ) - for cross in CROSSES_PLACED: - if cross.position == p: - return False - return True + global WID + other_side = (GAME_MODE + 1) % 2 + half = WID / 2 + min_x = other_side * half + max_x = min_x + half + return (min_x, max_x) + + +def crossFreeTile(x, y): + global CROSSES_PLACED + p = (x, y) + for cross in CROSSES_PLACED: + if cross.position == p: + return False + return True + def loadField(): - #Start a new game. - global TIME - global PLACED_BOATS - global SELECTED_TILES - global BOATS_TO_PLACE - global PLAYER_BOATS - global GAME_MODE - global GAME_MODE_PLACE_PLAYER_0 - global SELECTED_BOAT_TYPE - global PLAYER_0_HITS - global PLAYER_1_HITS - TIME = 0.0 - if PLACED_BOATS: - for boat in PLACED_BOATS: - ctDestroyObject( boat.tag ) - PLACED_BOATS = [] - SELECTED_TILES = [] - BOATS_TO_PLACE = PLAYER_BOATS[::] - SELECTED_BOAT_TYPE = BOATS_TO_PLACE.pop() - GAME_MODE = GAME_MODE_PLACE_PLAYER_0 - PLAYER_0_HITS = 0 - PLAYER_1_HITS = 0 - -def selectTile( x, y ): - global TILES - global SIZE - global SELECTED_TILES - tile = TILES[ y ][ x ] - SELECTED_TILES.append( tile ) - ctSetObjectImage( tile.tag, "water_selected.png", size = ( SIZE, SIZE ) ) - -def deselectTile( x, y ): - global TILES - global SIZE - global SELECTED_TILES - tile = TILES[ y ][ x ] - if tile in SELECTED_TILES: - SELECTED_TILES.remove( tile ) - ctSetObjectImage( tile.tag, "water.png", size = ( SIZE, SIZE ) ) + # Start a new game. + global TIME + global PLACED_BOATS + global SELECTED_TILES + global BOATS_TO_PLACE + global PLAYER_BOATS + global GAME_MODE + global GAME_MODE_PLACE_PLAYER_0 + global SELECTED_BOAT_TYPE + global PLAYER_0_HITS + global PLAYER_1_HITS + TIME = 0.0 + if PLACED_BOATS: + for boat in PLACED_BOATS: + ctDestroyObject(boat.tag) + PLACED_BOATS = [] + SELECTED_TILES = [] + BOATS_TO_PLACE = PLAYER_BOATS[::] + SELECTED_BOAT_TYPE = BOATS_TO_PLACE.pop() + GAME_MODE = GAME_MODE_PLACE_PLAYER_0 + PLAYER_0_HITS = 0 + PLAYER_1_HITS = 0 + + +def selectTile(x, y): + global TILES + global SIZE + global SELECTED_TILES + tile = TILES[y][x] + SELECTED_TILES.append(tile) + ctSetObjectImage(tile.tag, "water_selected.png", size=(SIZE, SIZE)) + + +def deselectTile(x, y): + global TILES + global SIZE + global SELECTED_TILES + tile = TILES[y][x] + if tile in SELECTED_TILES: + SELECTED_TILES.remove(tile) + ctSetObjectImage(tile.tag, "water.png", size=(SIZE, SIZE)) + def deselectTiles(): - global SELECTED_TILES - if SELECTED_TILES: - for tile in SELECTED_TILES: - if ctHasObject( tile.tag ) and not ctGetObjectHovered( tile.tag ): - deselectTile( tile.x, tile.y ) - -def boatFits( x, y, w = 1, h = 1, side = 0 ): - #Test if the boat fits at the selected tile. - global PLACED_BOATS - global WID - global HEI - min_x = ( WID / 2 ) * side - max_x = min_x + WID / 2 - if x < min_x or x + w > max_x or y < 0 or y + h > HEI: - return False - else: - for boat in PLACED_BOATS: - if ( x + w - 1 >= boat.x and x <= boat.x + boat.w - 1 - and y + h - 1 >= boat.y and y <= boat.y + boat.h - 1 ): - return False - return True + global SELECTED_TILES + if SELECTED_TILES: + for tile in SELECTED_TILES: + if ctHasObject(tile.tag) and not ctGetObjectHovered(tile.tag): + deselectTile(tile.x, tile.y) + + +def boatFits(x, y, w=1, h=1, side=0): + # Test if the boat fits at the selected tile. + global PLACED_BOATS + global WID + global HEI + min_x = (WID / 2) * side + max_x = min_x + WID / 2 + if x < min_x or x + w > max_x or y < 0 or y + h > HEI: + return False + else: + for boat in PLACED_BOATS: + if (x + w - 1 >= boat.x and x <= boat.x + boat.w - 1 + and y + h - 1 >= boat.y and y <= boat.y + boat.h - 1): + return False + return True + def updateHeldBoat(): - global SELECTED_BOAT_TYPE - global TILES - global GAME_MODE - mouse_position = ctGetMousePosition() - if ctHasObject( "cursor" ): - ctPlaceCenterObject( "cursor", mouse_position ); - bounds = getBoatTileBounds( SELECTED_BOAT_TYPE ) - mouse_tile = positionToCoord( mouse_position[ 0 ], mouse_position[ 1 ] ) - x = mouse_tile[ 0 ] + bounds[ 0 ] - y = mouse_tile[ 1 ] + bounds[ 1 ] - w = bounds[ 2 ] - h = bounds[ 3 ] - deselectTiles() - if boatFits( x, y, w, h, side = ( GAME_MODE % 2 ) ): - for ty in xrange( y, y + h ): - for tx in xrange( x, x + w ): - selectTile( tx, ty ) + global SELECTED_BOAT_TYPE + global TILES + global GAME_MODE + mouse_position = ctGetMousePosition() + if ctHasObject("cursor"): + ctPlaceCenterObject("cursor", mouse_position); + bounds = getBoatTileBounds(SELECTED_BOAT_TYPE) + mouse_tile = positionToCoord(mouse_position[0], mouse_position[1]) + x = mouse_tile[0] + bounds[0] + y = mouse_tile[1] + bounds[1] + w = bounds[2] + h = bounds[3] + deselectTiles() + if boatFits(x, y, w, h, side=(GAME_MODE % 2)): + for ty in xrange(y, y + h): + for tx in xrange(x, x + w): + selectTile(tx, ty) + def isGameOver(): - global PLAYER_0_HITS - global PLAYER_1_HITS - global PLAYER_HITS_NEEDED_TO_WIN - return ( PLAYER_0_HITS >= PLAYER_HITS_NEEDED_TO_WIN - or PLAYER_1_HITS >= PLAYER_HITS_NEEDED_TO_WIN ) + global PLAYER_0_HITS + global PLAYER_1_HITS + global PLAYER_HITS_NEEDED_TO_WIN + return (PLAYER_0_HITS >= PLAYER_HITS_NEEDED_TO_WIN + or PLAYER_1_HITS >= PLAYER_HITS_NEEDED_TO_WIN) + ## ## Implementation of the "mall.py" template file. ## def start(): - global TILES - global TILE_MAP - global WID - global HEI - global SIZE - TILES = [] - TILE_MAP = {} - for y in xrange( 0, HEI ): - xs = [] - TILES.append( xs ) - for x in xrange( 0, WID ): - tile = Tile( x, y ) - xs.append( tile ) - TILE_MAP[ tile.tag ] = tile - ctCreateObject( tile.tag, "water.png", position = coordToPosition( x, y ), size = ( SIZE, SIZE ) ) - ctCreateObject( "line", "line.png", position = coordToPosition( WID / 2.0 - 0.05, 0 ), size = ( 4, SIZE * ( HEI + GAP ) ) ) - updateCursor() - updateInstructions() - #Start a new game. - loadField() + global TILES + global TILE_MAP + global WID + global HEI + global SIZE + TILES = [] + TILE_MAP = {} + for y in xrange(0, HEI): + xs = [] + TILES.append(xs) + for x in xrange(0, WID): + tile = Tile(x, y) + xs.append(tile) + TILE_MAP[tile.tag] = tile + ctCreateObject(tile.tag, "water.png", position=coordToPosition(x, y), size=(SIZE, SIZE)) + ctCreateObject("line", "line.png", position=coordToPosition(WID / 2.0 - 0.05, 0), size=(4, SIZE * (HEI + GAP))) + updateCursor() + updateInstructions() + # Start a new game. + loadField() + def update(): - global TILES - global TIME - global PLACED_BOATS - r = ( math.cos( TIME ) * ( 180.0 / math.pi ) ) * 0.1 - TIME += 0.05 - for xs in TILES: - for tile in xs: - ctAngleObject( tile.tag, r ) - for boat in PLACED_BOATS: - if ctHasObject( boat.tag ): - ctAngleObject( boat.tag, r ) - pass - -def keyPressed( key ): - global ORIENTATION - global HORIZONTAL - global VERTICAL - if key == pygame.K_SPACE: - if ORIENTATION == HORIZONTAL: - ORIENTATION = VERTICAL - else: - ORIENTATION = HORIZONTAL - updateCursor() - -def mouseMoved( buttons ): - updateHeldBoat() - pass + global TILES + global TIME + global PLACED_BOATS + r = (math.cos(TIME) * (180.0 / math.pi)) * 0.1 + TIME += 0.05 + for xs in TILES: + for tile in xs: + ctAngleObject(tile.tag, r) + for boat in PLACED_BOATS: + if ctHasObject(boat.tag): + ctAngleObject(boat.tag, r) + pass + + +def keyPressed(key): + global ORIENTATION + global HORIZONTAL + global VERTICAL + if key == pygame.K_SPACE: + if ORIENTATION == HORIZONTAL: + ORIENTATION = VERTICAL + else: + ORIENTATION = HORIZONTAL + updateCursor() + + +def mouseMoved(buttons): + updateHeldBoat() + pass + def updateInstructions(): - global GAME_MODE - global GAME_MODE - global PLACED_BOATS - global CROSSES_PLACED - if PLACED_BOATS: - for boat in PLACED_BOATS: - ctSetObjectVisible( boat.tag, False ) - if CROSSES_PLACED: - for cross in CROSSES_PLACED: - ctSetObjectVisible( cross.tag, False ) - if GAME_MODE == 0: - ctCreateText( "player_text", "PLAYER 1 PLACE YOUR BOATS TO THE LEFT SIDE (Space to turn)" ) - elif GAME_MODE == 1: - ctSetObjectText( "player_text", "PLAYER 2 PLACE YOUR BOATS TO THE RIGHT SIDE (Space to turn)" ) - elif ( GAME_MODE % 2 ) == 0: - ctSetObjectText( "player_text", "PLAYER 1 ATTACK ON THE RIGHT SIDE" ) - elif ( GAME_MODE % 2 ) == 1: - ctSetObjectText( "player_text", "PLAYER 2 ATTACK ON THE LEFT SIDE" ) - -def raycast( name, tag, abs_pos, rel_pos ): - global TILE_MAP - global SIZE - global PLACED_BOATS - global ORIENTATION - global HORIZONTAL - global VERTICAL - global SELECTED_BOAT_TYPE - global BOATS_TO_PLACE - global PLAYER_BOATS - global GAME_MODE - global GAME_MODE_PLAY_PLAYER_0 - global SELECTED_BOAT_TYPE - global CROSSES_PLACED - global PLAYER_0_HITS - global PLAYER_1_HITS - global PLAYER_HITS_NEEDED_TO_WIN - if GAME_MODE < GAME_MODE_PLAY_PLAYER_0: - if "tile" in tag: - tile = TILE_MAP[ tag ] - bounds = getBoatTileBounds( SELECTED_BOAT_TYPE ) - x = tile.x + bounds[ 0 ] - y = tile.y + bounds[ 1 ] - w = + bounds[ 2 ] - h = + bounds[ 3 ] - img = getBoatImage( SELECTED_BOAT_TYPE ) - if boatFits( x, y, w, h, side = ( GAME_MODE % 2 ) ): - boat = Boat( x, y, w, h, GAME_MODE ) - PLACED_BOATS.append( boat ) - #Create a new ship at mouse position. - ctCreateObject( boat.tag , img, position = coordToPosition( x, y ), size = ( SIZE * w, SIZE * h ) ) - if( len( BOATS_TO_PLACE ) == 0 ): - #When the current player has placed all of his/her boats - #we move on to the next player. - GAME_MODE += 1 - if GAME_MODE < GAME_MODE_PLAY_PLAYER_0: - #The second player gets to place their boats. - BOATS_TO_PLACE = PLAYER_BOATS[::] - SELECTED_BOAT_TYPE = BOATS_TO_PLACE.pop() - updateInstructions() - else: - #All players have placed their boats, - #move on to the battle stage. - updateInstructions() - displayCurrentEnemies() - deselectTiles() - else: - SELECTED_BOAT_TYPE = BOATS_TO_PLACE.pop() - updateCursor() - elif not isGameOver(): - if "tile" in tag: - side = GAME_MODE % 2 - other_side = ( GAME_MODE + 1 ) % 2 - enemy_limits = getEnemyXLimits() - tile_coords = positionToCoord( abs_pos[ 0 ], abs_pos[ 1 ] ) - #Make sure the player aims on the enemy side. - if tile_coords[ 0 ] >= enemy_limits[ 0 ] and tile_coords[ 0 ] < enemy_limits[ 1 ]: - #Makde sure the player has not shoot on that spot before. - if crossFreeTile( tile_coords[ 0 ], tile_coords[ 1 ] ): - did_hit = not boatFits( tile_coords[ 0 ], tile_coords[ 1 ], 1, 1, side = other_side ) - #Put a marker on the shot tile. - cross = Cross( tile_coords, side, did_hit ) - ctCreateObject( cross.tag, cross.image, position = coordToPosition( cross.position[ 0 ], cross.position[ 1 ] ), size = ( SIZE, SIZE ) ) - CROSSES_PLACED.append( cross ) - if cross.did_hit: - #Give a point to the current player. - if side == 0: - PLAYER_0_HITS += 1 - if PLAYER_0_HITS >= PLAYER_HITS_NEEDED_TO_WIN: - #Player 0 must have hit all enemy ships, he/she wins. - ctCreateText( "winner_text", "PLAYER 1 WON!", position = ( 320, 320 ) ); - ctDestroyObject( "player_text" ); - else: - PLAYER_1_HITS += 1 - if PLAYER_1_HITS >= PLAYER_HITS_NEEDED_TO_WIN: - #Player 1 must have hit all enemy ships, he/she wins. - ctCreateText( "winner_text", "PLAYER 2 WON!", position = ( 320, 320 ) ); - ctDestroyObject( "player_text" ); - GAME_MODE += 1 - #Move on to the other player's turn. - updateInstructions() - displayCurrentEnemies() - -def raycastEntered( name, tag, abs_pos, rel_pos ): - global TILE_MAP - global GAME_MODE - global GAME_MODE_PLAY_PLAYER_0 - enemy_limits = getEnemyXLimits() - if GAME_MODE >= GAME_MODE_PLAY_PLAYER_0 and not isGameOver(): - #Allow for highlighting a enemy tile when in battle mode. - if "tile" in tag: - tile = TILE_MAP[ tag ] - if ( GAME_MODE < GAME_MODE_PLAY_PLAYER_0 - or tile.x >= enemy_limits[ 0 ] and tile.x < enemy_limits[ 1 ] ): - selectTile( tile.x, tile.y ) - else: - deselectTile( tile.x, tile.y ) - -def raycastExited( name, tag, abs_pos, rel_pos ): - global TILE_MAP - global GAME_MODE - global GAME_MODE_PLAY_PLAYER_0 - if GAME_MODE >= GAME_MODE_PLAY_PLAYER_0: - if "tile" in tag: - tile = TILE_MAP[ tag ] - deselectTile( tile.x, tile.y ) - -#Setup program and start. -ctSetup( 2 * OFF + WID * ( SIZE + GAP ), 2 * OFF + HEI * ( SIZE + GAP ), "Battleship", background = ( 49, 54, 152, 255 ), fps = 30 ) -ctBindFunctions( start, update, key_pressed = keyPressed, mouse_moved = mouseMoved, ray_cast = raycast, ray_cast_entered = raycastEntered, ray_cast_exited =raycastExited ) + global GAME_MODE + global GAME_MODE + global PLACED_BOATS + global CROSSES_PLACED + if PLACED_BOATS: + for boat in PLACED_BOATS: + ctSetObjectVisible(boat.tag, False) + if CROSSES_PLACED: + for cross in CROSSES_PLACED: + ctSetObjectVisible(cross.tag, False) + if GAME_MODE == 0: + ctCreateText("player_text", "PLAYER 1 PLACE YOUR BOATS TO THE LEFT SIDE (Space to turn)") + elif GAME_MODE == 1: + ctSetObjectText("player_text", "PLAYER 2 PLACE YOUR BOATS TO THE RIGHT SIDE (Space to turn)") + elif (GAME_MODE % 2) == 0: + ctSetObjectText("player_text", "PLAYER 1 ATTACK ON THE RIGHT SIDE") + elif (GAME_MODE % 2) == 1: + ctSetObjectText("player_text", "PLAYER 2 ATTACK ON THE LEFT SIDE") + + +def raycast(name, tag, abs_pos, rel_pos): + global TILE_MAP + global SIZE + global PLACED_BOATS + global ORIENTATION + global HORIZONTAL + global VERTICAL + global SELECTED_BOAT_TYPE + global BOATS_TO_PLACE + global PLAYER_BOATS + global GAME_MODE + global GAME_MODE_PLAY_PLAYER_0 + global SELECTED_BOAT_TYPE + global CROSSES_PLACED + global PLAYER_0_HITS + global PLAYER_1_HITS + global PLAYER_HITS_NEEDED_TO_WIN + if GAME_MODE < GAME_MODE_PLAY_PLAYER_0: + if "tile" in tag: + tile = TILE_MAP[tag] + bounds = getBoatTileBounds(SELECTED_BOAT_TYPE) + x = tile.x + bounds[0] + y = tile.y + bounds[1] + w = + bounds[2] + h = + bounds[3] + img = getBoatImage(SELECTED_BOAT_TYPE) + if boatFits(x, y, w, h, side=(GAME_MODE % 2)): + boat = Boat(x, y, w, h, GAME_MODE) + PLACED_BOATS.append(boat) + # Create a new ship at mouse position. + ctCreateObject(boat.tag, img, position=coordToPosition(x, y), size=(SIZE * w, SIZE * h)) + if (len(BOATS_TO_PLACE) == 0): + # When the current player has placed all of his/her boats + # we move on to the next player. + GAME_MODE += 1 + if GAME_MODE < GAME_MODE_PLAY_PLAYER_0: + # The second player gets to place their boats. + BOATS_TO_PLACE = PLAYER_BOATS[::] + SELECTED_BOAT_TYPE = BOATS_TO_PLACE.pop() + updateInstructions() + else: + # All players have placed their boats, + # move on to the battle stage. + updateInstructions() + displayCurrentEnemies() + deselectTiles() + else: + SELECTED_BOAT_TYPE = BOATS_TO_PLACE.pop() + updateCursor() + elif not isGameOver(): + if "tile" in tag: + side = GAME_MODE % 2 + other_side = (GAME_MODE + 1) % 2 + enemy_limits = getEnemyXLimits() + tile_coords = positionToCoord(abs_pos[0], abs_pos[1]) + # Make sure the player aims on the enemy side. + if tile_coords[0] >= enemy_limits[0] and tile_coords[0] < enemy_limits[1]: + # Makde sure the player has not shoot on that spot before. + if crossFreeTile(tile_coords[0], tile_coords[1]): + did_hit = not boatFits(tile_coords[0], tile_coords[1], 1, 1, side=other_side) + # Put a marker on the shot tile. + cross = Cross(tile_coords, side, did_hit) + ctCreateObject(cross.tag, cross.image, + position=coordToPosition(cross.position[0], cross.position[1]), size=(SIZE, SIZE)) + CROSSES_PLACED.append(cross) + if cross.did_hit: + # Give a point to the current player. + if side == 0: + PLAYER_0_HITS += 1 + if PLAYER_0_HITS >= PLAYER_HITS_NEEDED_TO_WIN: + # Player 0 must have hit all enemy ships, he/she wins. + ctCreateText("winner_text", "PLAYER 1 WON!", position=(320, 320)); + ctDestroyObject("player_text"); + else: + PLAYER_1_HITS += 1 + if PLAYER_1_HITS >= PLAYER_HITS_NEEDED_TO_WIN: + # Player 1 must have hit all enemy ships, he/she wins. + ctCreateText("winner_text", "PLAYER 2 WON!", position=(320, 320)); + ctDestroyObject("player_text"); + GAME_MODE += 1 + # Move on to the other player's turn. + updateInstructions() + displayCurrentEnemies() + + +def raycastEntered(name, tag, abs_pos, rel_pos): + global TILE_MAP + global GAME_MODE + global GAME_MODE_PLAY_PLAYER_0 + enemy_limits = getEnemyXLimits() + if GAME_MODE >= GAME_MODE_PLAY_PLAYER_0 and not isGameOver(): + # Allow for highlighting a enemy tile when in battle mode. + if "tile" in tag: + tile = TILE_MAP[tag] + if (GAME_MODE < GAME_MODE_PLAY_PLAYER_0 + or tile.x >= enemy_limits[0] and tile.x < enemy_limits[1]): + selectTile(tile.x, tile.y) + else: + deselectTile(tile.x, tile.y) + + +def raycastExited(name, tag, abs_pos, rel_pos): + global TILE_MAP + global GAME_MODE + global GAME_MODE_PLAY_PLAYER_0 + if GAME_MODE >= GAME_MODE_PLAY_PLAYER_0: + if "tile" in tag: + tile = TILE_MAP[tag] + deselectTile(tile.x, tile.y) + + +# Setup program and start. +ctSetup(2 * OFF + WID * (SIZE + GAP), 2 * OFF + HEI * (SIZE + GAP), "Battleship", background=(49, 54, 152, 255), fps=30) +ctBindFunctions(start, update, key_pressed=keyPressed, mouse_moved=mouseMoved, ray_cast=raycast, + ray_cast_entered=raycastEntered, ray_cast_exited=raycastExited) ctBegin() diff --git a/exercises/battleship/example/ct_pygame.py b/exercises/battleship/example/ct_pygame.py index 4be97e1..38e4838 100644 --- a/exercises/battleship/example/ct_pygame.py +++ b/exercises/battleship/example/ct_pygame.py @@ -1,19 +1,22 @@ -#CT_Pygame version 1.01 +# CT_Pygame version 1.01 -#Import needed modules. -import pygame, sys, time, math -from pygame.locals import * +# Import needed modules. +import math +import sys +import time -#Define several global variables used for setting up the window -#and keep track of the program state. +import pygame + +# Define several global variables used for setting up the window +# and keep track of the program state. __WINDOW = None __CANVAS = None __FONT = None __FONT_NAME = None __FONT_SIZE = 32 __WINDOW_TITLE = "" -__WINDOW_SIZE = ( 720, 480 ) -__BACKGROUND_COLOR = ( 0, 0, 0, 255 ) +__WINDOW_SIZE = (720, 480) +__BACKGROUND_COLOR = (0, 0, 0, 255) __OBJECTS = {} __RENDER_LIST = [] __LOADED_IMAGES = {} @@ -31,519 +34,586 @@ __RAY_CAST_ENTERED_FUNCTION = None __RAY_CAST_EXITED_FUNCTION = None __COLLISION_FUNCTION = None -__MOUSE_POSITION = ( 0, 0 ) +__MOUSE_POSITION = (0, 0) __DESTROYED_OBJECTS = [] __CREATED_OBJECTS = [] + # A class for storing a single object's properties. class ctObject: - def __init__( self, surface, position, size, rotation, name, solid, visible ): - self.surface = surface - self.position = position - self.size = size - self.rotation = rotation - self.hovered = False - self.solid = solid - self.visible = visible - self.name = name + def __init__(self, surface, position, size, rotation, name, solid, visible): + self.surface = surface + self.position = position + self.size = size + self.rotation = rotation + self.hovered = False + self.solid = solid + self.visible = visible + self.name = name + ## Pygame functions needed for setting up the library environment. def ctStartApp(): - global __WINDOW, __CANVAS - global __WINDOW_TITLE, __WINDOW_SIZE - pygame.init() - __WINDOW = pygame.display - __WINDOW.set_caption( __WINDOW_TITLE ) - __CANVAS = __WINDOW.set_mode( ( __WINDOW_SIZE ) ) - ctLoadFont() + global __WINDOW, __CANVAS + global __WINDOW_TITLE, __WINDOW_SIZE + pygame.init() + __WINDOW = pygame.display + __WINDOW.set_caption(__WINDOW_TITLE) + __CANVAS = __WINDOW.set_mode((__WINDOW_SIZE)) + ctLoadFont() + def ctEndApp(): - pygame.quit() + pygame.quit() + + +def ctLoadFont(font_path=None, font_size=32): + # Load a new font for use when creating text objects. + global __FONT + if font_path: + font_path = open(font_path) + __FONT = pygame.font.Font(font_path, font_size) -def ctLoadFont( font_path = None, font_size = 32 ): - #Load a new font for use when creating text objects. - global __FONT - if font_path: - font_path = open( font_path ) - __FONT = pygame.font.Font( font_path, font_size ) def ctMainLoop(): - global __OBJECTS, __DESTROYED_OBJECTS, __CREATED_OBJECTS, __WINDOW, __FPS, __MOUSE_POSITION, __KEYS_DOWN - global __UPDATE_FUNCTION, __KEY_PRESSED_FUNCTION, __KEY_RELEASED_FUNCTION, __KEY_HELD_FUNCTION - global __COLLISION_FUNCTION, __RAY_CAST_FUNCTION, __RAY_CAST_ENTERED_FUNCTION, __RAY_CAST_EXITED_FUNCTION - while True: - #Create marked objects. - if len( __CREATED_OBJECTS ) > 0: - for ( tag, obj ) in __CREATED_OBJECTS: - __OBJECTS[ tag ] = obj - __CREATED_OBJECTS = [] - #Since someone might attempt to bind new callback functions in mid game - #we will have to recalculate these booleans every iteration. - take_mouse_events = __RAY_CAST_FUNCTION or __RAY_CAST_ENTERED_FUNCTION or __RAY_CAST_EXITED_FUNCTION - #Iterate over all held keys. - if __KEY_HELD_FUNCTION: - ctCallUserHeldKeys() - #Process all new program events. - for event in pygame.event.get(): - #Quit if received a "quit" event. - if event.type == pygame.QUIT: - return - elif event.type == pygame.KEYDOWN: - __KEYS_DOWN[ event.key ] = True - ctCallUserKeyPressed( event.key ) - elif event.type == pygame.KEYUP: - __KEYS_DOWN.pop( event.key, None ) - ctCallUserKeyReleased( event.key ) - elif take_mouse_events: - #Only handle mouse events if at least one callback function is bound. - if event.type == pygame.MOUSEMOTION: - __MOUSE_POSITION = event.pos - ctCallUserMouseMoved( event.buttons ) - if __RAY_CAST_ENTERED_FUNCTION or __RAY_CAST_EXITED_FUNCTION: - ctRayCast( "auto:mouse", event.pos, hover_action = True ) - elif event.type == pygame.MOUSEBUTTONDOWN: - __MOUSE_POSITION = event.pos - ctCallUserMousePressed( event.button ) - if __RAY_CAST_FUNCTION: - ctRayCast( "auto:mouse", event.pos ) - elif event.type == pygame.MOUSEBUTTONUP: - __MOUSE_POSITION = event.pos - ctCallUserMousePressed( event.button ) - #Allow for the user to update the game. - if __UPDATE_FUNCTION: - ctCallUserUpdate() - #Test for collisions only when a callback is bound. - if __COLLISION_FUNCTION: - ctCollisionPass( "auto:collision" ) - #Draw the scene. - ctDrawScene() - #Destroy marked objects. - if len( __DESTROYED_OBJECTS ) > 0: - for tag in __DESTROYED_OBJECTS: - __OBJECTS.pop( tag, None ) - __DESTROYED_OBJECTS = [] - #Cap frame rate - time.sleep( 1.0 / __FPS ) + global __OBJECTS, __DESTROYED_OBJECTS, __CREATED_OBJECTS, __WINDOW, __FPS, __MOUSE_POSITION, __KEYS_DOWN + global __UPDATE_FUNCTION, __KEY_PRESSED_FUNCTION, __KEY_RELEASED_FUNCTION, __KEY_HELD_FUNCTION + global __COLLISION_FUNCTION, __RAY_CAST_FUNCTION, __RAY_CAST_ENTERED_FUNCTION, __RAY_CAST_EXITED_FUNCTION + while True: + # Create marked objects. + if len(__CREATED_OBJECTS) > 0: + for (tag, obj) in __CREATED_OBJECTS: + __OBJECTS[tag] = obj + __CREATED_OBJECTS = [] + # Since someone might attempt to bind new callback functions in mid game + # we will have to recalculate these booleans every iteration. + take_mouse_events = __RAY_CAST_FUNCTION or __RAY_CAST_ENTERED_FUNCTION or __RAY_CAST_EXITED_FUNCTION + # Iterate over all held keys. + if __KEY_HELD_FUNCTION: + ctCallUserHeldKeys() + # Process all new program events. + for event in pygame.event.get(): + # Quit if received a "quit" event. + if event.type == pygame.QUIT: + return + elif event.type == pygame.KEYDOWN: + __KEYS_DOWN[event.key] = True + ctCallUserKeyPressed(event.key) + elif event.type == pygame.KEYUP: + __KEYS_DOWN.pop(event.key, None) + ctCallUserKeyReleased(event.key) + elif take_mouse_events: + # Only handle mouse events if at least one callback function is bound. + if event.type == pygame.MOUSEMOTION: + __MOUSE_POSITION = event.pos + ctCallUserMouseMoved(event.buttons) + if __RAY_CAST_ENTERED_FUNCTION or __RAY_CAST_EXITED_FUNCTION: + ctRayCast("auto:mouse", event.pos, hover_action=True) + elif event.type == pygame.MOUSEBUTTONDOWN: + __MOUSE_POSITION = event.pos + ctCallUserMousePressed(event.button) + if __RAY_CAST_FUNCTION: + ctRayCast("auto:mouse", event.pos) + elif event.type == pygame.MOUSEBUTTONUP: + __MOUSE_POSITION = event.pos + ctCallUserMousePressed(event.button) + # Allow for the user to update the game. + if __UPDATE_FUNCTION: + ctCallUserUpdate() + # Test for collisions only when a callback is bound. + if __COLLISION_FUNCTION: + ctCollisionPass("auto:collision") + # Draw the scene. + ctDrawScene() + # Destroy marked objects. + if len(__DESTROYED_OBJECTS) > 0: + for tag in __DESTROYED_OBJECTS: + __OBJECTS.pop(tag, None) + __DESTROYED_OBJECTS = [] + # Cap frame rate + time.sleep(1.0 / __FPS) + ##Internal functions for callbacks. def ctCallUserStart(): - global __START_FUNCTION - if __START_FUNCTION: - __START_FUNCTION() + global __START_FUNCTION + if __START_FUNCTION: + __START_FUNCTION() + def ctCallUserUpdate(): - global __UPDATE_FUNCTION - if __UPDATE_FUNCTION: - __UPDATE_FUNCTION() + global __UPDATE_FUNCTION + if __UPDATE_FUNCTION: + __UPDATE_FUNCTION() + + +def ctCallUserKeyPressed(key): + global __KEY_PRESSED_FUNCTION + if __KEY_PRESSED_FUNCTION: + __KEY_PRESSED_FUNCTION(key) -def ctCallUserKeyPressed( key ): - global __KEY_PRESSED_FUNCTION - if __KEY_PRESSED_FUNCTION: - __KEY_PRESSED_FUNCTION( key ) -def ctCallUserKeyReleased( key ): - global __KEY_RELEASED_FUNCTION - if __KEY_RELEASED_FUNCTION: - __KEY_RELEASED_FUNCTION( key ) +def ctCallUserKeyReleased(key): + global __KEY_RELEASED_FUNCTION + if __KEY_RELEASED_FUNCTION: + __KEY_RELEASED_FUNCTION(key) + def ctCallUserHeldKeys(): - global __KEY_HELD_FUNCTION, __KEYS_DOWN - if __KEY_HELD_FUNCTION: - for key in __KEYS_DOWN: - __KEY_HELD_FUNCTION( key ) - -def ctCallUserMouseMoved( buttons ): - global __MOUSE_MOVED_FUNCTION - if __MOUSE_MOVED_FUNCTION: - __MOUSE_MOVED_FUNCTION( buttons ) - -def ctCallUserMousePressed( button ): - global __MOUSE_PRESSED_FUNCTION - if __MOUSE_PRESSED_FUNCTION: - __MOUSE_PRESSED_FUNCTION( button ) - -def ctCallUserMouseReleased( button ): - global __MOUSE_RELEASED_FUNCTION - if __MOUSE_RELEASED_FUNCTION: - __MOUSE_RELEASED_FUNCTION( button ) - -def ctCallUserRayCast( name, tag, abs_pos, rel_pos ): - global __RAY_CAST_FUNCTION - if __RAY_CAST_FUNCTION: - __RAY_CAST_FUNCTION( name, tag, abs_pos, rel_pos ) - -def ctCallUserRayCastEntered( name, tag, abs_pos, rel_pos ): - global __RAY_CAST_ENTERED_FUNCTION - if __RAY_CAST_ENTERED_FUNCTION: - __RAY_CAST_ENTERED_FUNCTION( name, tag, abs_pos, rel_pos ) - -def ctCallUserRayCastExited( name, tag, abs_pos, rel_pos ): - global __RAY_CAST_EXITED_FUNCTION - if __RAY_CAST_EXITED_FUNCTION: - __RAY_CAST_EXITED_FUNCTION( name, tag, abs_pos, rel_pos ) - -def ctCallUserCollision( name, tag0, tag1, correction ): - global __COLLISION_FUNCTION - if __COLLISION_FUNCTION: - __COLLISION_FUNCTION( name, tag0, tag1, correction ) + global __KEY_HELD_FUNCTION, __KEYS_DOWN + if __KEY_HELD_FUNCTION: + for key in __KEYS_DOWN: + __KEY_HELD_FUNCTION(key) + + +def ctCallUserMouseMoved(buttons): + global __MOUSE_MOVED_FUNCTION + if __MOUSE_MOVED_FUNCTION: + __MOUSE_MOVED_FUNCTION(buttons) + + +def ctCallUserMousePressed(button): + global __MOUSE_PRESSED_FUNCTION + if __MOUSE_PRESSED_FUNCTION: + __MOUSE_PRESSED_FUNCTION(button) + + +def ctCallUserMouseReleased(button): + global __MOUSE_RELEASED_FUNCTION + if __MOUSE_RELEASED_FUNCTION: + __MOUSE_RELEASED_FUNCTION(button) + + +def ctCallUserRayCast(name, tag, abs_pos, rel_pos): + global __RAY_CAST_FUNCTION + if __RAY_CAST_FUNCTION: + __RAY_CAST_FUNCTION(name, tag, abs_pos, rel_pos) + + +def ctCallUserRayCastEntered(name, tag, abs_pos, rel_pos): + global __RAY_CAST_ENTERED_FUNCTION + if __RAY_CAST_ENTERED_FUNCTION: + __RAY_CAST_ENTERED_FUNCTION(name, tag, abs_pos, rel_pos) + + +def ctCallUserRayCastExited(name, tag, abs_pos, rel_pos): + global __RAY_CAST_EXITED_FUNCTION + if __RAY_CAST_EXITED_FUNCTION: + __RAY_CAST_EXITED_FUNCTION(name, tag, abs_pos, rel_pos) + + +def ctCallUserCollision(name, tag0, tag1, correction): + global __COLLISION_FUNCTION + if __COLLISION_FUNCTION: + __COLLISION_FUNCTION(name, tag0, tag1, correction) + ## Functions for drawing onto the window. def ctClearCanvas(): - global __CANVAS - __CANVAS.fill( __BACKGROUND_COLOR ) + global __CANVAS + __CANVAS.fill(__BACKGROUND_COLOR) + def ctDrawScene(): - #Clear the window and draw all objects. - global __WINDOW, __RENDER_LIST, __CANVAS - ctClearCanvas() - for tag in __RENDER_LIST: - try: - obj = ctGetObject( tag ) - except KeyError: - continue - if obj.visible: - scaled_surface = pygame.transform.scale( obj.surface, obj.size ) - rotated_surface = pygame.transform.rotate( scaled_surface, obj.rotation ) - final_surface = rotated_surface - new_size = final_surface.get_size() - new_position = ( - obj.position[ 0 ] - ( new_size[ 0 ] - obj.size[ 0 ] ) / 2.0, - obj.position[ 1 ] - ( new_size[ 1 ] - obj.size[ 1 ] ) / 2.0 ) - __CANVAS.blit( final_surface, new_position ) - #Update the displayed window content. - __WINDOW.flip() - __WINDOW.update() + # Clear the window and draw all objects. + global __WINDOW, __RENDER_LIST, __CANVAS + ctClearCanvas() + for tag in __RENDER_LIST: + try: + obj = ctGetObject(tag) + except KeyError: + continue + if obj.visible: + scaled_surface = pygame.transform.scale(obj.surface, obj.size) + rotated_surface = pygame.transform.rotate(scaled_surface, obj.rotation) + final_surface = rotated_surface + new_size = final_surface.get_size() + new_position = ( + obj.position[0] - (new_size[0] - obj.size[0]) / 2.0, + obj.position[1] - (new_size[1] - obj.size[1]) / 2.0) + __CANVAS.blit(final_surface, new_position) + # Update the displayed window content. + __WINDOW.flip() + __WINDOW.update() + ## Helpful functions for "ray casting". -def ctGetCenter( position, size ): - return ( position[ 0 ] + size[ 0 ] / 2, position[ 1 ] + size[ 1 ] / 2 ) - -def ctRotatePointAround( position, center, r ): - #Rotate the point "position" around the point "center" with "r" deg. - radi = r * math.pi / 180.0 - c = math.cos( radi ) - s = math.sin( radi ) - dx = position[ 0 ] - center[ 0 ] - dy = position[ 1 ] - center[ 1 ] - nx = c * dx - s * dy + center[ 0 ] - ny = s * dx + c * dy + center[ 1 ] - return ( nx, ny ) - -def ctRayCast( name, position, hover_action = False ): - #Cast a ray (namned "name") into the window at "position" and call the configured callback function - #for each hit. Note that the name "auto:mouse" is used by the update loop. - global __RENDER_LIST - for tag in __RENDER_LIST: - try: - obj = ctGetObject( tag ) - except KeyError: - continue - center = ctGetCenter( obj.position, obj.size ) - off = ctRotatePointAround( position, center, obj.rotation ) - if ( off[ 0 ] >= obj.position[ 0 ] and off[ 0 ] <= obj.position[ 0 ] + obj.size[ 0 ] - and off[ 1 ] >= obj.position[ 1 ] and off[ 1 ] <= obj.position[ 1 ] + obj.size[ 1 ] ): - if hover_action and obj.hovered is False: - obj.hovered = True - ctCallUserRayCastEntered( name, tag, position, ( off[ 0 ] - obj.position[ 0 ], off[ 1 ] - obj.position[ 1 ] ) ) - if not hover_action: - ctCallUserRayCast( name, tag, position, ( off[ 0 ] - obj.position[ 0 ], off[ 1 ] - obj.position[ 1 ] ) ) - elif hover_action and obj.hovered: - obj.hovered = False - ctCallUserRayCastExited( name, tag, position, ( off[ 0 ] - obj.position[ 0 ], off[ 1 ] - obj.position[ 1 ] ) ) +def ctGetCenter(position, size): + return (position[0] + size[0] / 2, position[1] + size[1] / 2) + + +def ctRotatePointAround(position, center, r): + # Rotate the point "position" around the point "center" with "r" deg. + radi = r * math.pi / 180.0 + c = math.cos(radi) + s = math.sin(radi) + dx = position[0] - center[0] + dy = position[1] - center[1] + nx = c * dx - s * dy + center[0] + ny = s * dx + c * dy + center[1] + return (nx, ny) + + +def ctRayCast(name, position, hover_action=False): + # Cast a ray (namned "name") into the window at "position" and call the configured callback function + # for each hit. Note that the name "auto:mouse" is used by the update loop. + global __RENDER_LIST + for tag in __RENDER_LIST: + try: + obj = ctGetObject(tag) + except KeyError: + continue + center = ctGetCenter(obj.position, obj.size) + off = ctRotatePointAround(position, center, obj.rotation) + if (off[0] >= obj.position[0] and off[0] <= obj.position[0] + obj.size[0] + and off[1] >= obj.position[1] and off[1] <= obj.position[1] + obj.size[1]): + if hover_action and obj.hovered is False: + obj.hovered = True + ctCallUserRayCastEntered(name, tag, position, (off[0] - obj.position[0], off[1] - obj.position[1])) + if not hover_action: + ctCallUserRayCast(name, tag, position, (off[0] - obj.position[0], off[1] - obj.position[1])) + elif hover_action and obj.hovered: + obj.hovered = False + ctCallUserRayCastExited(name, tag, position, (off[0] - obj.position[0], off[1] - obj.position[1])) + ## Functions for collision. -def ctAABB( pos0, size0, r0, pos1, size1, r1 ): - #Perform a simple implementation of the "Separate normal axis theorem" (AABB-collision) - #on two rectangles defined by a position, size and rotation in deg. Will return "None" - #on no collision or a correction vector that should be applied to the "0" object in order - #to correct the placement so no collision occours. - def sub( a, b ): - return ( a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ] ) - def dot( a, b ): - return a[ 0 ] * b[ 0 ] + a[ 1 ] * b[ 1 ] - def norm( a ): - #Return normalized normal vector - l = math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] ) - return ( a[ 1 ] / l, - a[ 0 ] / l ) - def neg( a ): - return ( - a[ 0 ], - a[ 1 ] ) - def mul( f, a ): - return ( f * a[ 0 ], f * a[ 1 ] ) - center_a = ctGetCenter( pos0, size0 ) - center_b = ctGetCenter( pos1, size1 ) - a0 = ctRotatePointAround( pos0, center_a, r0 ) - a1 = ctRotatePointAround( ( pos0[ 0 ] + size0[ 0 ], pos0[ 1 ] ), center_a, r0 ) - a2 = ctRotatePointAround( ( pos0[ 0 ] + size0[ 0 ], pos0[ 1 ] + size0[ 1 ] ), center_a, r0 ) - a3 = ctRotatePointAround( ( pos0[ 0 ], pos0[ 1 ] + size0[ 1 ] ), center_a, r0 ) - ax = ( a0, a1, a2, a3 ) - b0 = ctRotatePointAround( pos1, center_b, r1 ) - b1 = ctRotatePointAround( ( pos1[ 0 ] + size1[ 0 ], pos1[ 1 ] ), center_b, r1 ) - b2 = ctRotatePointAround( ( pos1[ 0 ] + size1[ 0 ], pos1[ 1 ] + size1[ 1 ] ), center_b, r1 ) - b3 = ctRotatePointAround( ( pos1[ 0 ], pos1[ 1 ] + size1[ 1 ] ), center_b, r1 ) - bx = ( b0, b1, b2, b3 ) - #Yes, I know we actually only have to check two normals per object, - #but this was supposed to be a general implementation at some point. - #I can easily be rewritten to take convex polygon shapes if needed. - axs = ( - norm( sub( a1, a0 ) ), - norm( sub( a2, a1 ) ), - norm( sub( a3, a2 ) ), - norm( sub( a0, a3 ) ), - norm( sub( b1, b0 ) ), - norm( sub( b2, b1 ) ), - norm( sub( b3, b2 ) ), - norm( sub( b0, b3 ) ) ) - pss = ( a0, a1, a2, a3, b0, b1, b2, b3 ) - min_len = sys.float_info.max - min_norm = ( 1, 0 ) - for i in xrange( 0, 8 ): - d = axs[ i ] - p = pss[ i ] - min_a = sys.float_info.max - max_a = - sys.float_info.max - min_b = sys.float_info.max - max_b = - sys.float_info.max - for a in ax: - l = dot( d, sub( a, p ) ) - min_a = min( min_a, l ) - max_a = max( max_a, l ) - for b in bx: - l = dot( d, sub( b, p ) ) - min_b = min( min_b, l ) - max_b = max( max_b, l ) - if( max_a < min_b or min_a > max_b ): - return False - else: - len0 = ( max_a - min_b ) - len1 = ( max_b - min_a ) - if min( len0, len1 ) < min_len: - if len0 < len1: - min_len = len0 - min_norm = neg( d ) - else: - min_len = len1 - min_norm = d - return mul( min_len, min_norm ) - -def ctCollisionPass( name ): - #Will iterate through all objects and check for collisions. - #If any are found then the collision callback function will be - #called with "name" as the identifier. - global __OBJECTS - ls = list( __OBJECTS ) - ln = len( ls ) - for i in xrange( 0, ln ): - tag0 = ls[ i ] - obj0 = ctGetObject( tag0 ) - for n in xrange( i + 1, ln ): - tag1 = ls[ n ] - obj1 = ctGetObject( tag1 ) - #Make sure only solid object collide - if obj0.solid and obj1.solid: - res = ctAABB( obj0.position, obj0.size, - obj0.rotation, obj1.position, obj1.size, - obj1.rotation ) - if res: - ctCallUserCollision( name, tag0, tag1, res ) +def ctAABB(pos0, size0, r0, pos1, size1, r1): + # Perform a simple implementation of the "Separate normal axis theorem" (AABB-collision) + # on two rectangles defined by a position, size and rotation in deg. Will return "None" + # on no collision or a correction vector that should be applied to the "0" object in order + # to correct the placement so no collision occours. + def sub(a, b): + return (a[0] - b[0], a[1] - b[1]) + + def dot(a, b): + return a[0] * b[0] + a[1] * b[1] + + def norm(a): + # Return normalized normal vector + l = math.sqrt(a[0] * a[0] + a[1] * a[1]) + return (a[1] / l, - a[0] / l) + + def neg(a): + return (- a[0], - a[1]) + + def mul(f, a): + return (f * a[0], f * a[1]) + + center_a = ctGetCenter(pos0, size0) + center_b = ctGetCenter(pos1, size1) + a0 = ctRotatePointAround(pos0, center_a, r0) + a1 = ctRotatePointAround((pos0[0] + size0[0], pos0[1]), center_a, r0) + a2 = ctRotatePointAround((pos0[0] + size0[0], pos0[1] + size0[1]), center_a, r0) + a3 = ctRotatePointAround((pos0[0], pos0[1] + size0[1]), center_a, r0) + ax = (a0, a1, a2, a3) + b0 = ctRotatePointAround(pos1, center_b, r1) + b1 = ctRotatePointAround((pos1[0] + size1[0], pos1[1]), center_b, r1) + b2 = ctRotatePointAround((pos1[0] + size1[0], pos1[1] + size1[1]), center_b, r1) + b3 = ctRotatePointAround((pos1[0], pos1[1] + size1[1]), center_b, r1) + bx = (b0, b1, b2, b3) + # Yes, I know we actually only have to check two normals per object, + # but this was supposed to be a general implementation at some point. + # I can easily be rewritten to take convex polygon shapes if needed. + axs = ( + norm(sub(a1, a0)), + norm(sub(a2, a1)), + norm(sub(a3, a2)), + norm(sub(a0, a3)), + norm(sub(b1, b0)), + norm(sub(b2, b1)), + norm(sub(b3, b2)), + norm(sub(b0, b3))) + pss = (a0, a1, a2, a3, b0, b1, b2, b3) + min_len = sys.float_info.max + min_norm = (1, 0) + for i in xrange(0, 8): + d = axs[i] + p = pss[i] + min_a = sys.float_info.max + max_a = - sys.float_info.max + min_b = sys.float_info.max + max_b = - sys.float_info.max + for a in ax: + l = dot(d, sub(a, p)) + min_a = min(min_a, l) + max_a = max(max_a, l) + for b in bx: + l = dot(d, sub(b, p)) + min_b = min(min_b, l) + max_b = max(max_b, l) + if (max_a < min_b or min_a > max_b): + return False + else: + len0 = (max_a - min_b) + len1 = (max_b - min_a) + if min(len0, len1) < min_len: + if len0 < len1: + min_len = len0 + min_norm = neg(d) + else: + min_len = len1 + min_norm = d + return mul(min_len, min_norm) + + +def ctCollisionPass(name): + # Will iterate through all objects and check for collisions. + # If any are found then the collision callback function will be + # called with "name" as the identifier. + global __OBJECTS + ls = list(__OBJECTS) + ln = len(ls) + for i in xrange(0, ln): + tag0 = ls[i] + obj0 = ctGetObject(tag0) + for n in xrange(i + 1, ln): + tag1 = ls[n] + obj1 = ctGetObject(tag1) + # Make sure only solid object collide + if obj0.solid and obj1.solid: + res = ctAABB(obj0.position, obj0.size, - obj0.rotation, obj1.position, obj1.size, - obj1.rotation) + if res: + ctCallUserCollision(name, tag0, tag1, res) + ## Functions for loading graphics. -def ctGetImage( image_path ): - global __LOADED_IMAGES - try: - return __LOADED_IMAGES[ image_path ] - except KeyError: - img = pygame.image.load( image_path ).convert_alpha() - __LOADED_IMAGES[ image_path ] = img - return img +def ctGetImage(image_path): + global __LOADED_IMAGES + try: + return __LOADED_IMAGES[image_path] + except KeyError: + img = pygame.image.load(image_path).convert_alpha() + __LOADED_IMAGES[image_path] = img + return img + + +def ctGetTextImage(message, color): + global __FONT + return __FONT.render(message, 1, color) -def ctGetTextImage( message, color ): - global __FONT - return __FONT.render( message, 1, color ) ## Function for creating and manipulating objects. -def ctGetObject( tag ): - global __OBJECTS, __CREATED_OBJECTS - try: - return __OBJECTS[ tag ] - except KeyError: - for ( t, o ) in __CREATED_OBJECTS: - if tag == t: - return o - return None - -def ctCreateObject( tag, image_path, position = ( 0, 0 ), size = None, rotation = 0.0, name = "", solid = True, visible = True ): - #Creates a new image object with the identifier in "tag" by loading the image found at "image_path". - ctCreateEntity( tag, ctGetImage( image_path ), position, size, rotation, name, solid, visible ) - -def ctCreateText( tag, message = "", color = ( 255, 255, 255, 255 ), position = ( 0, 0 ), size = None, rotation = 0.0, name = None, solid = True, visible = True ): - if name == None: - name = message - #Creates a new text object with the identifier in "tag" with the text passed as "message". - ctCreateEntity( tag, ctGetTextImage( message, color ), position, size, rotation, name, solid, visible ) - -def ctCreateEntity( tag, surface, position, size, rotation, name, solid, visible ): - #Not to be used outside the wrapper module. - global __CREATED_OBJECTS, __RENDER_LIST - if size == None: - size = surface.get_size() - obj = ctObject( surface, position, size, rotation, name, solid, visible ) - __CREATED_OBJECTS.append( ( tag, obj ) ) - __RENDER_LIST.append( tag ) - -def ctDestroyObject( tag ): - #Destroys a object given the tag in "tag". - global __DESTROYED_OBJECTS, __RENDER_LIST - __DESTROYED_OBJECTS.append( tag ) - __RENDER_LIST.remove( tag ) - -def ctSetObjectImage( tag, image_path, size = None ): - #Swaps the object image with a new image. - obj = ctGetObject( tag ) - obj.surface = ctGetImage( image_path ) - if size == None: - obj.size = obj.surface.get_size() - -def ctSetObjectText( tag, message = "", color = ( 255, 255, 255, 255 ), size = None ): - #Swaps the object image with a new image. - obj = ctGetObject( tag ) - obj.surface = ctGetTextImage( message, color ) - if size == None: - obj.size = obj.surface.get_size() - -def ctPlaceObject( tag, new_position ): - #Places the object with the identifier in "tag" at position (x, y). - ctGetObject( tag ).position = new_position - -def ctPlaceCenterObject( tag, new_position ): - #Places the center of object with the identifier in "tag" at position (x, y). - obj = ctGetObject( tag ) - obj.position = ( - new_position[ 0 ] - obj.size[ 0 ] / 2, - new_position[ 1 ] - obj.size[ 1 ] / 2 ) - -def ctMoveObject( tag, direction ): - #Moves the object with the identifier in "tag" with the offset (dx, dy). - obj = ctGetObject( tag ) - obj.position = ( - obj.position[ 0 ] + direction[ 0 ], - obj.position[ 1 ] + direction[ 1 ] ) - -def ctMoveObjectInDirection( tag, direction ): - #Moves the object with the identifier in "tag" with the object local offset (dx, dy). - obj = ctGetObject( tag ) - radi = obj.rotation * math.pi / 180.0 - radi2 = radi + math.pi / 2.0 - obj.position = ( - obj.position[ 0 ] + ( direction[ 0 ] * math.cos( radi ) + direction[ 1 ] * math.cos( radi2 ) ), - obj.position[ 1 ] - ( direction[ 0 ] * math.sin( radi ) + direction[ 1 ] * math.sin( radi2 ) ) ) - -def ctAngleObject( tag, new_angle ): - #Sets the object rotation with the identifier in "tag" to "new_angle" in deg. - ctGetObject( tag ).rotation = new_angle - -def ctRotateObject( tag, angle ): - #Rotates the object rotation with the identifier in "tag" with "angle" in deg. - ctGetObject( tag ).rotation += angle - -def ctSizeObject( tag, new_size ): - #Sets the object size with the identifier in "tag" to "new_size" (width, height). - ctGetObject( tag ).size = new_size +def ctGetObject(tag): + global __OBJECTS, __CREATED_OBJECTS + try: + return __OBJECTS[tag] + except KeyError: + for (t, o) in __CREATED_OBJECTS: + if tag == t: + return o + return None + + +def ctCreateObject(tag, image_path, position=(0, 0), size=None, rotation=0.0, name="", solid=True, visible=True): + # Creates a new image object with the identifier in "tag" by loading the image found at "image_path". + ctCreateEntity(tag, ctGetImage(image_path), position, size, rotation, name, solid, visible) + + +def ctCreateText(tag, message="", color=(255, 255, 255, 255), position=(0, 0), size=None, rotation=0.0, name=None, + solid=True, visible=True): + if name == None: + name = message + # Creates a new text object with the identifier in "tag" with the text passed as "message". + ctCreateEntity(tag, ctGetTextImage(message, color), position, size, rotation, name, solid, visible) + + +def ctCreateEntity(tag, surface, position, size, rotation, name, solid, visible): + # Not to be used outside the wrapper module. + global __CREATED_OBJECTS, __RENDER_LIST + if size == None: + size = surface.get_size() + obj = ctObject(surface, position, size, rotation, name, solid, visible) + __CREATED_OBJECTS.append((tag, obj)) + __RENDER_LIST.append(tag) + + +def ctDestroyObject(tag): + # Destroys a object given the tag in "tag". + global __DESTROYED_OBJECTS, __RENDER_LIST + __DESTROYED_OBJECTS.append(tag) + __RENDER_LIST.remove(tag) + + +def ctSetObjectImage(tag, image_path, size=None): + # Swaps the object image with a new image. + obj = ctGetObject(tag) + obj.surface = ctGetImage(image_path) + if size == None: + obj.size = obj.surface.get_size() + + +def ctSetObjectText(tag, message="", color=(255, 255, 255, 255), size=None): + # Swaps the object image with a new image. + obj = ctGetObject(tag) + obj.surface = ctGetTextImage(message, color) + if size == None: + obj.size = obj.surface.get_size() + + +def ctPlaceObject(tag, new_position): + # Places the object with the identifier in "tag" at position (x, y). + ctGetObject(tag).position = new_position + + +def ctPlaceCenterObject(tag, new_position): + # Places the center of object with the identifier in "tag" at position (x, y). + obj = ctGetObject(tag) + obj.position = ( + new_position[0] - obj.size[0] / 2, + new_position[1] - obj.size[1] / 2) + + +def ctMoveObject(tag, direction): + # Moves the object with the identifier in "tag" with the offset (dx, dy). + obj = ctGetObject(tag) + obj.position = ( + obj.position[0] + direction[0], + obj.position[1] + direction[1]) + + +def ctMoveObjectInDirection(tag, direction): + # Moves the object with the identifier in "tag" with the object local offset (dx, dy). + obj = ctGetObject(tag) + radi = obj.rotation * math.pi / 180.0 + radi2 = radi + math.pi / 2.0 + obj.position = ( + obj.position[0] + (direction[0] * math.cos(radi) + direction[1] * math.cos(radi2)), + obj.position[1] - (direction[0] * math.sin(radi) + direction[1] * math.sin(radi2))) + + +def ctAngleObject(tag, new_angle): + # Sets the object rotation with the identifier in "tag" to "new_angle" in deg. + ctGetObject(tag).rotation = new_angle + + +def ctRotateObject(tag, angle): + # Rotates the object rotation with the identifier in "tag" with "angle" in deg. + ctGetObject(tag).rotation += angle + + +def ctSizeObject(tag, new_size): + # Sets the object size with the identifier in "tag" to "new_size" (width, height). + ctGetObject(tag).size = new_size + ## Functions for fetching object values. -def ctGetObjectPosition( tag ): - return ctGetObject( tag ).position +def ctGetObjectPosition(tag): + return ctGetObject(tag).position + + +def ctSetObjectPosition(tag, position): + ctGetObject(tag).position = position + + +def ctGetObjectSize(tag): + return ctGetObject(tag).size + + +def ctSetObjectSize(tag, size): + ctGetObject(tag).size = size + + +def ctGetObjectRotation(tag): + return ctGetObject(tag).rotation -def ctSetObjectPosition( tag, position ): - ctGetObject( tag ).position = position -def ctGetObjectSize( tag ): - return ctGetObject( tag ).size +def ctSetObjectRotation(tag, rotation): + ctGetObject(tag).rotation = rotation -def ctSetObjectSize( tag, size ): - ctGetObject( tag ).size = size -def ctGetObjectRotation( tag ): - return ctGetObject( tag ).rotation +def ctGetObjectName(tag): + return ctGetObject(tag).name -def ctSetObjectRotation( tag, rotation ): - ctGetObject( tag ).rotation = rotation -def ctGetObjectName( tag ): - return ctGetObject( tag ).name +def ctSetObjectName(tag, name): + ctGetObject(tag).name = name -def ctSetObjectName( tag, name ): - ctGetObject( tag ).name = name -def ctGetObjectHovered( tag ): - #Note that this property only makes sense if - #at least one of the hover callbacks have been set. - return ctGetObject( tag ).hovered +def ctGetObjectHovered(tag): + # Note that this property only makes sense if + # at least one of the hover callbacks have been set. + return ctGetObject(tag).hovered -def ctGetObjectSolid( tag ): - return ctGetObject( tag ).solid -def ctSetObjectSolid( tag, solid ): - ctGetObject( tag ).solid = solid +def ctGetObjectSolid(tag): + return ctGetObject(tag).solid -def ctGetObjectVisible( tag ): - return ctGetObject( tag ).visible -def ctSetObjectVisible( tag, visible ): - ctGetObject( tag ).visible = visible +def ctSetObjectSolid(tag, solid): + ctGetObject(tag).solid = solid + + +def ctGetObjectVisible(tag): + return ctGetObject(tag).visible + + +def ctSetObjectVisible(tag, visible): + ctGetObject(tag).visible = visible + + +def ctHasObject(tag): + return ctGetObject(tag) != None -def ctHasObject( tag ): - return ctGetObject( tag ) != None ## Functions for getting some handy values. def ctGetMousePosition(): - global __MOUSE_POSITION - return __MOUSE_POSITION + global __MOUSE_POSITION + return __MOUSE_POSITION + def ctGetWindowSize(): - global __WINDOW - return __WINDOW.get_size() + global __WINDOW + return __WINDOW.get_size() + ## Function for binding callback functions. -def ctSetup( width, height, title = "", background = ( 0, 0, 0, 255 ), fps = 30.0 ): - #Allows for setting some inital values used by the program window. - global __WINDOW_TITLE, __WINDOW_SIZE, __BACKGROUND_COLOR, __FPS - __WINDOW_TITLE = title - __WINDOW_SIZE = ( int( width ), int( height ) ) - __BACKGROUND_COLOR = background - __FPS = min( max( 1, fps ), 128 ) - -def ctBindFunctions( start = None, update = None, key_pressed = None, key_released = None, key_held = None, mouse_moved = None, mouse_pressed = None, mouse_released = None, ray_cast = None, ray_cast_entered = None, ray_cast_exited = None, collision = None ): - #Binds each argument as a callback function. If a callback function is set to "None" that type of event will not be generated. - #NOTE: This function should be called before "ctBegin". - global __START_FUNCTION, __UPDATE_FUNCTION - global __KEY_PRESSED_FUNCTION, __KEY_RELEASED_FUNCTION, __KEY_HELD_FUNCTION - global __MOUSE_MOVED_FUNCTION, __MOUSE_PRESSED_FUNCTION, __MOUSE_RELEASED_FUNCTION - global __RAY_CAST_FUNCTION, __RAY_CAST_ENTERED_FUNCTION, __RAY_CAST_EXITED_FUNCTION, __COLLISION_FUNCTION - __START_FUNCTION = start - __UPDATE_FUNCTION = update - __KEY_PRESSED_FUNCTION = key_pressed - __KEY_RELEASED_FUNCTION = key_released - __KEY_HELD_FUNCTION = key_held - __MOUSE_MOVED_FUNCTION = mouse_moved - __MOUSE_PRESSED_FUNCTION = mouse_pressed - __MOUSE_RELEASED_FUNCTION = mouse_released - __RAY_CAST_FUNCTION = ray_cast - __RAY_CAST_ENTERED_FUNCTION = ray_cast_entered - __RAY_CAST_EXITED_FUNCTION = ray_cast_exited - __COLLISION_FUNCTION = collision +def ctSetup(width, height, title="", background=(0, 0, 0, 255), fps=30.0): + # Allows for setting some inital values used by the program window. + global __WINDOW_TITLE, __WINDOW_SIZE, __BACKGROUND_COLOR, __FPS + __WINDOW_TITLE = title + __WINDOW_SIZE = (int(width), int(height)) + __BACKGROUND_COLOR = background + __FPS = min(max(1, fps), 128) + + +def ctBindFunctions(start=None, update=None, key_pressed=None, key_released=None, key_held=None, mouse_moved=None, + mouse_pressed=None, mouse_released=None, ray_cast=None, ray_cast_entered=None, ray_cast_exited=None, + collision=None): + # Binds each argument as a callback function. If a callback function is set to "None" that type of event will not be generated. + # NOTE: This function should be called before "ctBegin". + global __START_FUNCTION, __UPDATE_FUNCTION + global __KEY_PRESSED_FUNCTION, __KEY_RELEASED_FUNCTION, __KEY_HELD_FUNCTION + global __MOUSE_MOVED_FUNCTION, __MOUSE_PRESSED_FUNCTION, __MOUSE_RELEASED_FUNCTION + global __RAY_CAST_FUNCTION, __RAY_CAST_ENTERED_FUNCTION, __RAY_CAST_EXITED_FUNCTION, __COLLISION_FUNCTION + __START_FUNCTION = start + __UPDATE_FUNCTION = update + __KEY_PRESSED_FUNCTION = key_pressed + __KEY_RELEASED_FUNCTION = key_released + __KEY_HELD_FUNCTION = key_held + __MOUSE_MOVED_FUNCTION = mouse_moved + __MOUSE_PRESSED_FUNCTION = mouse_pressed + __MOUSE_RELEASED_FUNCTION = mouse_released + __RAY_CAST_FUNCTION = ray_cast + __RAY_CAST_ENTERED_FUNCTION = ray_cast_entered + __RAY_CAST_EXITED_FUNCTION = ray_cast_exited + __COLLISION_FUNCTION = collision + def ctBegin(): - #Starts the program. - #NOTE: This function should be called (only once) after "ctBindFunctions". - ctStartApp() - ctCallUserStart() - ctMainLoop() - ctEndApp() + # Starts the program. + # NOTE: This function should be called (only once) after "ctBindFunctions". + ctStartApp() + ctCallUserStart() + ctMainLoop() + ctEndApp() From 1acecd6b871d984d7b76c7e166bd87306a44cb9e Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 29 Sep 2018 14:29:31 +0200 Subject: [PATCH 07/47] Replaced xrange with range. --- exercises/battleship/example/battleship.py | 8 ++++---- exercises/battleship/example/ct_pygame.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/exercises/battleship/example/battleship.py b/exercises/battleship/example/battleship.py index 60a7548..4ced3cf 100644 --- a/exercises/battleship/example/battleship.py +++ b/exercises/battleship/example/battleship.py @@ -258,8 +258,8 @@ def updateHeldBoat(): h = bounds[3] deselectTiles() if boatFits(x, y, w, h, side=(GAME_MODE % 2)): - for ty in xrange(y, y + h): - for tx in xrange(x, x + w): + for ty in range(y, y + h): + for tx in range(x, x + w): selectTile(tx, ty) @@ -283,10 +283,10 @@ def start(): global SIZE TILES = [] TILE_MAP = {} - for y in xrange(0, HEI): + for y in range(0, HEI): xs = [] TILES.append(xs) - for x in xrange(0, WID): + for x in range(0, WID): tile = Tile(x, y) xs.append(tile) TILE_MAP[tile.tag] = tile diff --git a/exercises/battleship/example/ct_pygame.py b/exercises/battleship/example/ct_pygame.py index 38e4838..95bacb9 100644 --- a/exercises/battleship/example/ct_pygame.py +++ b/exercises/battleship/example/ct_pygame.py @@ -332,7 +332,7 @@ def mul(f, a): pss = (a0, a1, a2, a3, b0, b1, b2, b3) min_len = sys.float_info.max min_norm = (1, 0) - for i in xrange(0, 8): + for i in range(0, 8): d = axs[i] p = pss[i] min_a = sys.float_info.max @@ -369,10 +369,10 @@ def ctCollisionPass(name): global __OBJECTS ls = list(__OBJECTS) ln = len(ls) - for i in xrange(0, ln): + for i in range(0, ln): tag0 = ls[i] obj0 = ctGetObject(tag0) - for n in xrange(i + 1, ln): + for n in range(i + 1, ln): tag1 = ls[n] obj1 = ctGetObject(tag1) # Make sure only solid object collide From a50a3a69c74e3a54f361c80fd630fd6831bd37d9 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 29 Sep 2018 14:37:54 +0200 Subject: [PATCH 08/47] Reformat code. --- exercises/chat/online_examples/pairclient.py | 12 +-- exercises/chat/online_examples/pairserver.py | 12 +-- exercises/chat/online_examples/pub_server.py | 16 ++-- exercises/chat/online_examples/sub_client.py | 26 +++--- exercises/chat/zmq_chat.py | 89 ++++++++++++-------- 5 files changed, 89 insertions(+), 66 deletions(-) diff --git a/exercises/chat/online_examples/pairclient.py b/exercises/chat/online_examples/pairclient.py index 22bf374..4c4e5a8 100644 --- a/exercises/chat/online_examples/pairclient.py +++ b/exercises/chat/online_examples/pairclient.py @@ -1,10 +1,9 @@ -#http://learning-0mq-with-pyzmq.readthedocs.org/en/latest/pyzmq/patterns/pair.html +# http://learning-0mq-with-pyzmq.readthedocs.org/en/latest/pyzmq/patterns/pair.html -import zmq -import random -import sys import time +import zmq + port = "5556" context = zmq.Context() socket = context.socket(zmq.PAIR) @@ -12,7 +11,8 @@ while True: msg = socket.recv() - print msg + print + msg socket.send("client message to server1") socket.send("client message to server2") - time.sleep(1) \ No newline at end of file + time.sleep(1) diff --git a/exercises/chat/online_examples/pairserver.py b/exercises/chat/online_examples/pairserver.py index 1a7ae2e..6997c14 100644 --- a/exercises/chat/online_examples/pairserver.py +++ b/exercises/chat/online_examples/pairserver.py @@ -1,10 +1,9 @@ -#http://learning-0mq-with-pyzmq.readthedocs.org/en/latest/pyzmq/patterns/pair.html +# http://learning-0mq-with-pyzmq.readthedocs.org/en/latest/pyzmq/patterns/pair.html -import zmq -import random -import sys import time +import zmq + port = "5556" context = zmq.Context() socket = context.socket(zmq.PAIR) @@ -13,5 +12,6 @@ while True: socket.send("Server message to client3") msg = socket.recv() - print msg - time.sleep(1) \ No newline at end of file + print + msg + time.sleep(1) diff --git a/exercises/chat/online_examples/pub_server.py b/exercises/chat/online_examples/pub_server.py index 317f3ad..54cf9f7 100644 --- a/exercises/chat/online_examples/pub_server.py +++ b/exercises/chat/online_examples/pub_server.py @@ -1,13 +1,14 @@ -#http://learning-0mq-with-pyzmq.readthedocs.org/en/latest/pyzmq/patterns/pubsub.html +# http://learning-0mq-with-pyzmq.readthedocs.org/en/latest/pyzmq/patterns/pubsub.html -import zmq import random import sys import time +import zmq + port = "5556" if len(sys.argv) > 1: - port = sys.argv[1] + port = sys.argv[1] int(port) context = zmq.Context() @@ -15,8 +16,9 @@ socket.bind("tcp://*:%s" % port) while True: - topic = random.randrange(9999,10005) - messagedata = random.randrange(1,215) - 80 - print "%d %d" % (topic, messagedata) + topic = random.randrange(9999, 10005) + messagedata = random.randrange(1, 215) - 80 + print + "%d %d" % (topic, messagedata) socket.send("%d %d" % (topic, messagedata)) - time.sleep(1) \ No newline at end of file + time.sleep(1) diff --git a/exercises/chat/online_examples/sub_client.py b/exercises/chat/online_examples/sub_client.py index af181b6..1010946 100644 --- a/exercises/chat/online_examples/sub_client.py +++ b/exercises/chat/online_examples/sub_client.py @@ -1,27 +1,28 @@ -#http://learning-0mq-with-pyzmq.readthedocs.org/en/latest/pyzmq/patterns/pubsub.html +# http://learning-0mq-with-pyzmq.readthedocs.org/en/latest/pyzmq/patterns/pubsub.html import sys + import zmq port = "5556" if len(sys.argv) > 1: - port = sys.argv[1] + port = sys.argv[1] int(port) - + if len(sys.argv) > 2: - port1 = sys.argv[2] + port1 = sys.argv[2] int(port1) # Socket to talk to server context = zmq.Context() socket = context.socket(zmq.SUB) -print "Collecting updates from weather server..." -socket.connect ("tcp://localhost:%s" % port) +print +"Collecting updates from weather server..." +socket.connect("tcp://localhost:%s" % port) if len(sys.argv) > 2: - socket.connect ("tcp://localhost:%s" % port1) - + socket.connect("tcp://localhost:%s" % port1) # Subscribe to zipcode, default is NYC, 10001 topicfilter = "10001" @@ -29,11 +30,12 @@ # Process 5 updates total_value = 0 -for update_nbr in range (5): +for update_nbr in range(5): string = socket.recv() topic, messagedata = string.split() total_value += int(messagedata) - print topic, messagedata + print + topic, messagedata -print "Average messagedata value for topic '%s' was %dF" % (topicfilter, total_value / update_nbr) - \ No newline at end of file +print +"Average messagedata value for topic '%s' was %dF" % (topicfilter, total_value / update_nbr) diff --git a/exercises/chat/zmq_chat.py b/exercises/chat/zmq_chat.py index 96ab539..65a2acd 100644 --- a/exercises/chat/zmq_chat.py +++ b/exercises/chat/zmq_chat.py @@ -1,8 +1,12 @@ - -import sys, time -import zmq, socket +import socket +import sys +import time from multiprocessing import Process, Lock -#print zmq.pyzmq_version() + +import zmq + + +# print zmq.pyzmq_version() def receive_loop(connect_to, channel): @@ -10,33 +14,38 @@ def receive_loop(connect_to, channel): local_context = zmq.Context() subscribe = local_context.socket(zmq.SUB) subscribe.setsockopt(zmq.SUBSCRIBE, channel) - + try: subscribe.connect(connect_to) except zmq.error.ZMQError: - print "## Trouble connecting... :P Check if the adress is correct." + print + "## Trouble connecting... :P Check if the adress is correct." return - + while True: - #print "Awating message..." - print subscribe.recv() + # print "Awating message..." + print + subscribe.recv() time.sleep(0.005) + def start_listener(connect_to): """Creates a new daemon listener thread to this client on this channel (ie topic). Stores thread in `connections`.""" global filter p = Process(target=receive_loop, args=(connect_to, filter)) p.daemon = True - + c_lock.acquire() - connections.append( {'process':p, 'channel':filter, 'client':connect_to} ) + connections.append({'process': p, 'channel': filter, 'client': connect_to}) c_lock.release() - + p.start() + def connect(): """Parses input for an ip and a port. Uses the input to start a connection.""" - print "Connect to..." + print + "Connect to..." address = raw_input("address (ip): ") if address == "": address = "localhost" @@ -44,32 +53,36 @@ def connect(): port = int(raw_input("port: ")) connect_to = "tcp://%s:%s" % (address, port) except: - print "## Error! Should look like '192.168.0.1 5556'" + print + "## Error! Should look like '192.168.0.1 5556'" return - + start_listener(connect_to) def help(): """Prints available commands and what they do.""" - print """ - Commands: - \\exit\tExits the program - \\help\tPrints this help - \\connect\tConnect to another chat client - \\disconnect \tDisconnects from all clients using this channel - \\channel \tWrites to this chat channel only""" + print + """ + Commands: + \\exit\tExits the program + \\help\tPrints this help + \\connect\tConnect to another chat client + \\disconnect \tDisconnects from all clients using this channel + \\channel \tWrites to this chat channel only""" + def disconnect(channel): """Stops all processes that listens on a certain channel.""" c_lock.acquire() - #print "## Disconnecting from '"+channel+"'..." + # print "## Disconnecting from '"+channel+"'..." for entry in connections: if entry['channel'] == channel: connections.remove(entry) entry['process'].terminate() c_lock.release() + def io_loop(): """Loop for main (IO) thread. Handles user input such as commands and chat messages to send.""" help() @@ -77,7 +90,7 @@ def io_loop(): global filter input = raw_input() if input.startswith('\\'): - #command + # command if input.startswith('\\exit'): break elif input.startswith('\\connect'): @@ -86,23 +99,27 @@ def io_loop(): try: channel = input.split(' ')[1] except: - print "## Type '\\disconnect '" + print + "## Type '\\disconnect '" disconnect(channel) elif input.startswith('\\channel'): try: filter = input.split(' ')[1] except: - print "## Type '\\channel '" + print + "## Type '\\channel '" elif input.startswith('\\help'): help() else: - print "## Unrecognized command %s. Type `\\help` to see what commands are available." % input + print + "## Unrecognized command %s. Type `\\help` to see what commands are available." % input else: - tracker = publish.send("%s> %s" % (filter, input), copy = False, track = True) + tracker = publish.send("%s> %s" % (filter, input), copy=False, track=True) tracker.wait(5) if not tracker.done: - print "## Timeout after 5 sec... :P" - + print + "## Timeout after 5 sec... :P" + # Sockets terminate implicitly at garbage collection # Would be done here otherwise @@ -115,13 +132,15 @@ def io_loop(): if __name__ == '__main__': main_context = zmq.Context() publish = main_context.socket(zmq.PUB) - + port = 5556 if len(sys.argv) > 1: port = sys.argv[1] - + publish.bind("tcp://*:%s" % port) - print "Local IP:", socket.gethostbyname(socket.gethostname()) - print "Port:", port - + print + "Local IP:", socket.gethostbyname(socket.gethostname()) + print + "Port:", port + io_loop() From 30e33189e29d85137f59799c155d609320ee1802 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 29 Sep 2018 14:39:43 +0200 Subject: [PATCH 09/47] Refractored raw_input to input in zmq_chat.py --- exercises/chat/zmq_chat.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/exercises/chat/zmq_chat.py b/exercises/chat/zmq_chat.py index 65a2acd..0e846f9 100644 --- a/exercises/chat/zmq_chat.py +++ b/exercises/chat/zmq_chat.py @@ -18,14 +18,12 @@ def receive_loop(connect_to, channel): try: subscribe.connect(connect_to) except zmq.error.ZMQError: - print - "## Trouble connecting... :P Check if the adress is correct." + print("## Trouble connecting... :P Check if the adress is correct.") return while True: # print "Awating message..." - print - subscribe.recv() + print(subscribe.recv()) time.sleep(0.005) @@ -44,13 +42,12 @@ def start_listener(connect_to): def connect(): """Parses input for an ip and a port. Uses the input to start a connection.""" - print - "Connect to..." - address = raw_input("address (ip): ") + print("Connect to...") + address = input("address (ip): ") if address == "": address = "localhost" try: - port = int(raw_input("port: ")) + port = int(input("port: ")) connect_to = "tcp://%s:%s" % (address, port) except: print @@ -88,7 +85,7 @@ def io_loop(): help() while True: global filter - input = raw_input() + input = input() if input.startswith('\\'): # command if input.startswith('\\exit'): From 562ea7788ffede7d18b08bdb46621dc9bcfe78df Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 29 Sep 2018 14:42:20 +0200 Subject: [PATCH 10/47] Add parenthesis to prints. --- exercises/chat/online_examples/pairclient.py | 3 +-- exercises/chat/online_examples/pairserver.py | 3 +-- exercises/chat/online_examples/pub_server.py | 3 +-- exercises/chat/online_examples/sub_client.py | 9 +++---- exercises/chat/zmq_chat.py | 25 +++++++------------- 5 files changed, 15 insertions(+), 28 deletions(-) diff --git a/exercises/chat/online_examples/pairclient.py b/exercises/chat/online_examples/pairclient.py index 4c4e5a8..7e46144 100644 --- a/exercises/chat/online_examples/pairclient.py +++ b/exercises/chat/online_examples/pairclient.py @@ -11,8 +11,7 @@ while True: msg = socket.recv() - print - msg + print(msg) socket.send("client message to server1") socket.send("client message to server2") time.sleep(1) diff --git a/exercises/chat/online_examples/pairserver.py b/exercises/chat/online_examples/pairserver.py index 6997c14..988d0db 100644 --- a/exercises/chat/online_examples/pairserver.py +++ b/exercises/chat/online_examples/pairserver.py @@ -12,6 +12,5 @@ while True: socket.send("Server message to client3") msg = socket.recv() - print - msg + print(msg) time.sleep(1) diff --git a/exercises/chat/online_examples/pub_server.py b/exercises/chat/online_examples/pub_server.py index 54cf9f7..9318067 100644 --- a/exercises/chat/online_examples/pub_server.py +++ b/exercises/chat/online_examples/pub_server.py @@ -18,7 +18,6 @@ while True: topic = random.randrange(9999, 10005) messagedata = random.randrange(1, 215) - 80 - print - "%d %d" % (topic, messagedata) + print("%d %d" % (topic, messagedata)) socket.send("%d %d" % (topic, messagedata)) time.sleep(1) diff --git a/exercises/chat/online_examples/sub_client.py b/exercises/chat/online_examples/sub_client.py index 1010946..a0346ba 100644 --- a/exercises/chat/online_examples/sub_client.py +++ b/exercises/chat/online_examples/sub_client.py @@ -17,8 +17,7 @@ context = zmq.Context() socket = context.socket(zmq.SUB) -print -"Collecting updates from weather server..." +print("Collecting updates from weather server...") socket.connect("tcp://localhost:%s" % port) if len(sys.argv) > 2: @@ -34,8 +33,6 @@ string = socket.recv() topic, messagedata = string.split() total_value += int(messagedata) - print - topic, messagedata + print(topic, messagedata) -print -"Average messagedata value for topic '%s' was %dF" % (topicfilter, total_value / update_nbr) +print("Average messagedata value for topic '%s' was %dF" % (topicfilter, total_value / update_nbr)) diff --git a/exercises/chat/zmq_chat.py b/exercises/chat/zmq_chat.py index 0e846f9..23cbe85 100644 --- a/exercises/chat/zmq_chat.py +++ b/exercises/chat/zmq_chat.py @@ -59,20 +59,19 @@ def connect(): def help(): """Prints available commands and what they do.""" - print - """ + print(""" Commands: \\exit\tExits the program \\help\tPrints this help \\connect\tConnect to another chat client \\disconnect \tDisconnects from all clients using this channel - \\channel \tWrites to this chat channel only""" + \\channel \tWrites to this chat channel only""") def disconnect(channel): """Stops all processes that listens on a certain channel.""" c_lock.acquire() - # print "## Disconnecting from '"+channel+"'..." + # print("## Disconnecting from '"+channel+"'...") for entry in connections: if entry['channel'] == channel: connections.remove(entry) @@ -96,26 +95,22 @@ def io_loop(): try: channel = input.split(' ')[1] except: - print - "## Type '\\disconnect '" + print("## Type '\\disconnect '") disconnect(channel) elif input.startswith('\\channel'): try: filter = input.split(' ')[1] except: - print - "## Type '\\channel '" + print("## Type '\\channel '") elif input.startswith('\\help'): help() else: - print - "## Unrecognized command %s. Type `\\help` to see what commands are available." % input + print("## Unrecognized command %s. Type `\\help` to see what commands are available." % input) else: tracker = publish.send("%s> %s" % (filter, input), copy=False, track=True) tracker.wait(5) if not tracker.done: - print - "## Timeout after 5 sec... :P" + print("## Timeout after 5 sec... :P") # Sockets terminate implicitly at garbage collection # Would be done here otherwise @@ -135,9 +130,7 @@ def io_loop(): port = sys.argv[1] publish.bind("tcp://*:%s" % port) - print - "Local IP:", socket.gethostbyname(socket.gethostname()) - print - "Port:", port + print("Local IP:", socket.gethostbyname(socket.gethostname())) + print("Port:", port) io_loop() From 44ba25bbdff2fbdcaccc0c14622d770d51d66d88 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 29 Sep 2018 14:44:16 +0200 Subject: [PATCH 11/47] Renamed input variable to inp to avoid name conflict. --- exercises/chat/zmq_chat.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/exercises/chat/zmq_chat.py b/exercises/chat/zmq_chat.py index 23cbe85..0b2434e 100644 --- a/exercises/chat/zmq_chat.py +++ b/exercises/chat/zmq_chat.py @@ -84,30 +84,30 @@ def io_loop(): help() while True: global filter - input = input() - if input.startswith('\\'): + inp = input() + if inp.startswith('\\'): # command - if input.startswith('\\exit'): + if inp.startswith('\\exit'): break - elif input.startswith('\\connect'): + elif inp.startswith('\\connect'): connect() - elif input.startswith('\\disconnect'): + elif inp.startswith('\\disconnect'): try: - channel = input.split(' ')[1] + channel = inp.split(' ')[1] except: print("## Type '\\disconnect '") disconnect(channel) - elif input.startswith('\\channel'): + elif inp.startswith('\\channel'): try: - filter = input.split(' ')[1] + filter = inp.split(' ')[1] except: print("## Type '\\channel '") - elif input.startswith('\\help'): + elif inp.startswith('\\help'): help() else: - print("## Unrecognized command %s. Type `\\help` to see what commands are available." % input) + print("## Unrecognized command %s. Type `\\help` to see what commands are available." % inp) else: - tracker = publish.send("%s> %s" % (filter, input), copy=False, track=True) + tracker = publish.send("%s> %s" % (filter, inp), copy=False, track=True) tracker.wait(5) if not tracker.done: print("## Timeout after 5 sec... :P") From 216396a4b833d1871d35078d5b19546b82bad0a1 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 29 Sep 2018 14:49:20 +0200 Subject: [PATCH 12/47] Add missing parenthesis to a print. --- exercises/chat/zmq_chat.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/exercises/chat/zmq_chat.py b/exercises/chat/zmq_chat.py index 0b2434e..20e034c 100644 --- a/exercises/chat/zmq_chat.py +++ b/exercises/chat/zmq_chat.py @@ -50,8 +50,7 @@ def connect(): port = int(input("port: ")) connect_to = "tcp://%s:%s" % (address, port) except: - print - "## Error! Should look like '192.168.0.1 5556'" + print("## Error! Should look like '192.168.0.1 5556'") return start_listener(connect_to) From 34311391cfdf94678ccc4e5e28241f0152e5e030 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 29 Sep 2018 14:57:44 +0200 Subject: [PATCH 13/47] Updated to work with current version of zmq library. --- exercises/chat/online_examples/pairserver.py | 2 +- exercises/chat/online_examples/pub_server.py | 2 +- exercises/chat/online_examples/sub_client.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exercises/chat/online_examples/pairserver.py b/exercises/chat/online_examples/pairserver.py index 988d0db..83e5235 100644 --- a/exercises/chat/online_examples/pairserver.py +++ b/exercises/chat/online_examples/pairserver.py @@ -10,7 +10,7 @@ socket.bind("tcp://*:%s" % port) while True: - socket.send("Server message to client3") + socket.send_string("Server message to client3") msg = socket.recv() print(msg) time.sleep(1) diff --git a/exercises/chat/online_examples/pub_server.py b/exercises/chat/online_examples/pub_server.py index 9318067..641251f 100644 --- a/exercises/chat/online_examples/pub_server.py +++ b/exercises/chat/online_examples/pub_server.py @@ -19,5 +19,5 @@ topic = random.randrange(9999, 10005) messagedata = random.randrange(1, 215) - 80 print("%d %d" % (topic, messagedata)) - socket.send("%d %d" % (topic, messagedata)) + socket.send_string("%d %d" % (topic, messagedata)) time.sleep(1) diff --git a/exercises/chat/online_examples/sub_client.py b/exercises/chat/online_examples/sub_client.py index a0346ba..cd55a9b 100644 --- a/exercises/chat/online_examples/sub_client.py +++ b/exercises/chat/online_examples/sub_client.py @@ -25,7 +25,7 @@ # Subscribe to zipcode, default is NYC, 10001 topicfilter = "10001" -socket.setsockopt(zmq.SUBSCRIBE, topicfilter) +socket.setsockopt_string(zmq.SUBSCRIBE, topicfilter) # Process 5 updates total_value = 0 From 6b234292a6834e51ba3dd9640b449df91b6c9ff6 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 15 Dec 2018 16:27:49 +0100 Subject: [PATCH 14/47] Fibonacci now works with python 3 --- .../fibonacci/fibonacci-iterative-extended.py | 8 ++-- exercises/fibonacci/fibonacci-iterative.py | 47 ++++++++++--------- exercises/fibonacci/fibonacci-recursive.py | 6 +-- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/exercises/fibonacci/fibonacci-iterative-extended.py b/exercises/fibonacci/fibonacci-iterative-extended.py index 5175f54..f13d84d 100644 --- a/exercises/fibonacci/fibonacci-iterative-extended.py +++ b/exercises/fibonacci/fibonacci-iterative-extended.py @@ -9,17 +9,17 @@ def main(): try: number = int(input) except ValueError: - print "Please enter a valid integer!" + print("Please enter a valid integer!") input = get_input() continue if number >= fibonacci[-1]: - print next_fibonacci(number) + print(next_fibonacci(number)) else: - print find_next_fibonacci(number) + print(find_next_fibonacci(number)) input = get_input() def get_input(): - return raw_input("Enter a number or 'exit' to quit: ") + return input("Enter a number or 'exit' to quit: ") def next_fibonacci(stop_after): """Iteratively generates the fibonacci sequence, starting where the currently saved series ends, until the fibonacci number that diff --git a/exercises/fibonacci/fibonacci-iterative.py b/exercises/fibonacci/fibonacci-iterative.py index 71ce861..8512d97 100644 --- a/exercises/fibonacci/fibonacci-iterative.py +++ b/exercises/fibonacci/fibonacci-iterative.py @@ -1,35 +1,38 @@ '''An iterative version, perhaps more intuitive for beginners.''' -input = int(raw_input("Enter a number: ")); +input = int(input("Enter a number: ")) + def fibonacci_n(stop_after): """Iteratively searches for the N-th fibonacci number""" if stop_after <= 0: - return 0; + return 0 if stop_after <= 2: - return 1; - - prev = 1; - curr = 1; - count = 2; - + return 1 + + prev = 1 + curr = 1 + count = 2 + while count <= stop_after: - curr = curr + prev; - prev = curr - prev; - count = count + 1; - - return prev; + curr = curr + prev + prev = curr - prev + count = count + 1 + + return prev + def next_fibonacci(stop_after): """Iteratively searches for the fibonacci number that comes after the stop_after value""" - prev = 0; - curr = 1; - + prev = 0 + curr = 1 + while prev <= stop_after: - curr += prev; - prev = curr - prev; - - return prev; - -print next_fibonacci(input); + curr += prev + prev = curr - prev + + return prev + + +print(next_fibonacci(input)) diff --git a/exercises/fibonacci/fibonacci-recursive.py b/exercises/fibonacci/fibonacci-recursive.py index ff220be..938360f 100644 --- a/exercises/fibonacci/fibonacci-recursive.py +++ b/exercises/fibonacci/fibonacci-recursive.py @@ -1,8 +1,8 @@ -input = int(raw_input("Enter a number: ")); +input = int(input("Enter a number: ")); # Alternatively: # input = input("Enter a number; "); # The difference is that input() parameter is evaluated as a command, -# whereas raw_input() evaluates the parameter as a string +# whereas input() evaluates the parameter as a string def fibonacci_n(prev, curr, stop_after): @@ -27,4 +27,4 @@ def next_fibonacci(prev, curr, stop_after): return prev; return next_fibonacci(curr, curr + prev, stop_after) -print next_fibonacci(0, 1, input); \ No newline at end of file +print(next_fibonacci(0, 1, input)) \ No newline at end of file From cc0054000189467c0449e35353344ea411fda19a Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 15 Dec 2018 18:09:16 +0100 Subject: [PATCH 15/47] Now works with python 3 --- exercises/personnr/personnr.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/personnr/personnr.py b/exercises/personnr/personnr.py index 356d0ed..5ab7d6d 100644 --- a/exercises/personnr/personnr.py +++ b/exercises/personnr/personnr.py @@ -76,8 +76,8 @@ def check(str): if len(sys.argv) > 1: pnr = sys.argv[1] else: - print "Uses default number: ", pnr + print("Uses default number: ", pnr) a = check(pnr) -print a[1] +print(a[1]) From 96b6fed4376402218576afa0abea74f88f2216b1 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Thu, 17 Jan 2019 15:13:45 +0100 Subject: [PATCH 16/47] Fixed python2 problems. --- exercises/midi/midi.py | 8 ++++---- exercises/midi/midi2.py | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/exercises/midi/midi.py b/exercises/midi/midi.py index 74d6ee8..f963d41 100644 --- a/exercises/midi/midi.py +++ b/exercises/midi/midi.py @@ -69,17 +69,17 @@ def main(): pygame.init() midi.init() - print "Number of MIDI devices:", midi.get_count() + print("Number of MIDI devices:", midi.get_count()) if midi.get_count() == 0: - print "No MIDI devices detected :P" + print("No MIDI devices detected :P") return out_device = midi.get_default_output_id() if out_device == -1: - print "No MIDI output device detected :P" + print("No MIDI output device detected :P") return - print "Uses device no:", out_device + print("Uses device no:", out_device) try: output = midi.Output( out_device ) output.set_instrument( instrument ) diff --git a/exercises/midi/midi2.py b/exercises/midi/midi2.py index 4e864fc..311830b 100644 --- a/exercises/midi/midi2.py +++ b/exercises/midi/midi2.py @@ -67,30 +67,30 @@ def incr_volume(self): self.volume = self.volume + 5 if self.volume > 125: self.volume = 125 - print "Volume", self.volume + print("Volume", self.volume) def decr_volume(self): """Decrease volume (-5). Min == 0""" self.volume = self.volume - 5 if self.volume < 0: self.volume = 0 - print "Volume", self.volume + print("Volume", self.volume) def incr_instrument(self): """Change instrument (+1)""" self.instrument = (self.instrument + 1)%128 self.out.set_instrument(self.instrument) - print "Instrument", self.instrument + print("Instrument", self.instrument) def decr_instrument(self): """Change instrument (-1)""" self.instrument = (self.instrument - 1)%128 self.out.set_instrument(self.instrument) - print "Instrument", self.instrument + print("Instrument", self.instrument) def help(): """Help function.""" - print """ + print(""") ------------------------------------- # Midi keyboard. @@ -101,7 +101,7 @@ def help(): Increase/decrease volume: 'b'/'n' Press 'h' to see this help again. --------------------------------------""" +-------------------------------------""") def io_loop(keyboard): """Main program loop. Handles IO.""" @@ -145,17 +145,17 @@ def main(): pygame.init() midi.init() - print "Number of MIDI devices:", midi.get_count() + print("Number of MIDI devices:", midi.get_count()) if midi.get_count() == 0: - print "No MIDI devices detected :P" + print("No MIDI devices detected :P") return out_device = midi.get_default_output_id() if out_device == -1: - print "No MIDI output device detected :P" + print("No MIDI output device detected :P") return - print "Uses device no:", out_device + print("Uses device no:", out_device) keyboard = Keyboard(out_device, instrument) try: From a08ef15cb6d87c8150dff91c0fd68555c0ad3357 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Mon, 21 Jan 2019 11:13:53 +0100 Subject: [PATCH 17/47] Removed rogue parenthesis. --- exercises/midi/midi2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/midi/midi2.py b/exercises/midi/midi2.py index 311830b..ad8b01c 100644 --- a/exercises/midi/midi2.py +++ b/exercises/midi/midi2.py @@ -90,7 +90,7 @@ def decr_instrument(self): def help(): """Help function.""" - print(""") + print(""" ------------------------------------- # Midi keyboard. From 845a0d8fe94b63ab3cdce49d92735cec2ba7c1cf Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Thu, 14 Feb 2019 13:56:32 +0100 Subject: [PATCH 18/47] Simplified readme somewhat. --- exercises/fibonacci/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/fibonacci/README.md b/exercises/fibonacci/README.md index 7002030..a22e276 100644 --- a/exercises/fibonacci/README.md +++ b/exercises/fibonacci/README.md @@ -6,16 +6,16 @@ En applikation som tar ett tal som input, och skriver ut det första [fibonacci- ## Delmoment -1. Börja med en loop som hela tiden, med hjälp av några variabler, räknar ut nästa fibonacci-tal och skriver ut det. Loopen behöver bara gå tillräckligt många varv för att du ska kunna se om den skriver ut rätt talföljd. **Svårighetsgrad:** 1 +1. Börja med en [for](https://wiki.python.org/moin/ForLoop)\-loop som skriver ut fibonacci-tal på skärmen, börja med 0, 1, 1 ... osv. Loopen behöver endast gå tillräckligt länge för att bekräfta att den skriver ut rätt talföljd (exempelvis 15 varv eller så). **Svårighetsgrad:** 1 2. Gör så att loopen, istället för att snurra ett visst antal varv, sluter när den har räknat ut ett fibonacci-tal som överstiger något tal (spelar inte så stor roll vilket tal just nu). **Svårighetsgrad:** 1 -3. Låt nu användaren att under körning mata in ett tal och byt ut talet i förra delmomentet mot detta istället. **Svårighetsgrad:** 1 +3. Låt nu användaren få mata in ett tal i terminalen och byt ut talet i förra delmomentet mot detta istället. **Svårighetsgrad:** 1 4. Ta bort utskriften av alla fibonacci-tal i loopen och skriv endast ut resultatet när det slutgiltiga talet är framtaget. **Svårighetsgrad:** 1 5. Skapa en funktion som tar emot ett argument och flytta in hela loopen. Funktionen ska returnera det värde som förut skrevs ut. Exempelvis ska `fibonacci(10)` returnera 13. **Svårighetsgrad:** 1 -6. Se till så att programmet anropar funktionen med talet du får som input som argument och skriver ut resultatet. **Svårighetsgrad:** 1 +6. Se till så att programmet anropar funktionen med talet du får av användaren som argument och skriver ut resultatet. **Svårighetsgrad:** 1 ## Utbyggnad - Låt användaren kunna ange input-talet som ett argument till programmet, exempelvis: `python fibonacci.py 35` -- Prova att generera talserien [rekursivt](http://www.sparknotes.com/cs/recursion/whatisrecursion/section1.rhtml) istället för iterativt. **Svårighetsgrad:** 2 +- Prova att generera talserien [rekursivt](https://sv.wikipedia.org/wiki/Rekursion) istället för iterativt. **Svårighetsgrad:** 2 - Få programmet att fråga och svara användaren om och om igen istället för bara en gång. Spara dessutom den uträknade talserien i en lista, så att om användaren frågar om ett tal mindre än det största talet i den sparade talserien behöver talserien inte räknas ut på nytt. Istället letar man igenom listan efter svaret. Frågar däremot användaren om ett tal som är större än eller lika med det största talet i den sparade talserien så fortsätter man att generera talserien från slutet av den sparade talserien istället för att börja om på nytt. **Svårighetsgrad:** 2 \ No newline at end of file From 15189df404aa0b71e015c1f8bd18270d13ba14fb Mon Sep 17 00:00:00 2001 From: pilino1234 Date: Thu, 14 Feb 2019 13:57:46 +0100 Subject: [PATCH 19/47] Update exercises/chat/online_examples/pairclient.py Co-Authored-By: ViddeM --- exercises/chat/online_examples/pairclient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/chat/online_examples/pairclient.py b/exercises/chat/online_examples/pairclient.py index 7e46144..4ea9b61 100644 --- a/exercises/chat/online_examples/pairclient.py +++ b/exercises/chat/online_examples/pairclient.py @@ -7,7 +7,7 @@ port = "5556" context = zmq.Context() socket = context.socket(zmq.PAIR) -socket.connect("tcp://localhost:%s" % port) +socket.connect("tcp://localhost:{}".format(port)) while True: msg = socket.recv() From b56872c4dde6e5a43cb30105af17f2c63d1b9046 Mon Sep 17 00:00:00 2001 From: pilino1234 Date: Thu, 14 Feb 2019 13:57:54 +0100 Subject: [PATCH 20/47] Update exercises/chat/online_examples/pairserver.py Co-Authored-By: ViddeM --- exercises/chat/online_examples/pairserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/chat/online_examples/pairserver.py b/exercises/chat/online_examples/pairserver.py index 83e5235..9438da0 100644 --- a/exercises/chat/online_examples/pairserver.py +++ b/exercises/chat/online_examples/pairserver.py @@ -7,7 +7,7 @@ port = "5556" context = zmq.Context() socket = context.socket(zmq.PAIR) -socket.bind("tcp://*:%s" % port) +socket.bind("tcp://*:{}".format(port)) while True: socket.send_string("Server message to client3") From b9057c1e3368b5090eee3472ae555b88df3bdb86 Mon Sep 17 00:00:00 2001 From: pilino1234 Date: Thu, 14 Feb 2019 13:58:00 +0100 Subject: [PATCH 21/47] Update exercises/chat/online_examples/pub_server.py Co-Authored-By: ViddeM --- exercises/chat/online_examples/pub_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/chat/online_examples/pub_server.py b/exercises/chat/online_examples/pub_server.py index 641251f..d320a37 100644 --- a/exercises/chat/online_examples/pub_server.py +++ b/exercises/chat/online_examples/pub_server.py @@ -13,7 +13,7 @@ context = zmq.Context() socket = context.socket(zmq.PUB) -socket.bind("tcp://*:%s" % port) +socket.bind("tcp://*:{}".format(port)) while True: topic = random.randrange(9999, 10005) From a2e9d4fd291b5a266c4476b82e8d07b772e4e146 Mon Sep 17 00:00:00 2001 From: pilino1234 Date: Thu, 14 Feb 2019 13:58:31 +0100 Subject: [PATCH 22/47] Update exercises/chat/online_examples/pub_server.py Co-Authored-By: ViddeM --- exercises/chat/online_examples/pub_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/chat/online_examples/pub_server.py b/exercises/chat/online_examples/pub_server.py index d320a37..f571553 100644 --- a/exercises/chat/online_examples/pub_server.py +++ b/exercises/chat/online_examples/pub_server.py @@ -18,6 +18,6 @@ while True: topic = random.randrange(9999, 10005) messagedata = random.randrange(1, 215) - 80 - print("%d %d" % (topic, messagedata)) + print(topic, messagedata) socket.send_string("%d %d" % (topic, messagedata)) time.sleep(1) From 7a5871a5d93ff4b4e43151667f144cad42041c5b Mon Sep 17 00:00:00 2001 From: pilino1234 Date: Thu, 14 Feb 2019 13:58:39 +0100 Subject: [PATCH 23/47] Update exercises/chat/online_examples/pub_server.py Co-Authored-By: ViddeM --- exercises/chat/online_examples/pub_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/chat/online_examples/pub_server.py b/exercises/chat/online_examples/pub_server.py index f571553..5eb1d73 100644 --- a/exercises/chat/online_examples/pub_server.py +++ b/exercises/chat/online_examples/pub_server.py @@ -19,5 +19,5 @@ topic = random.randrange(9999, 10005) messagedata = random.randrange(1, 215) - 80 print(topic, messagedata) - socket.send_string("%d %d" % (topic, messagedata)) + socket.send_string("{} {}".format(topic, messagedata)) time.sleep(1) From cc2e2ca580ac6f7a3a2ec856c723c527d0a2e5ae Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Thu, 14 Feb 2019 15:06:38 +0100 Subject: [PATCH 24/47] Updated code and readme slightly. --- exercises/chat/README.md | 6 +++--- exercises/chat/online_examples/pub_server.py | 4 ++-- exercises/chat/online_examples/sub_client.py | 7 +++---- exercises/chat/zmq_chat.py | 12 ++++++------ 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/exercises/chat/README.md b/exercises/chat/README.md index 77b2b1f..1d99948 100644 --- a/exercises/chat/README.md +++ b/exercises/chat/README.md @@ -5,7 +5,7 @@ Enkel chat i terminalen mellan två (eller flera) datorer utan server. **Koncept**: nätverk, trådar **Tips**: Programmet testas som enklast lokalt på samma dator mellan två terminal-fönster. Som i exemplen kan man då använda -`zmq_socket.connect("tcp://localhost:%s" % port)` +`zmq_socket.connect("tcp://localhost:%{}".format(port))` för att ansluta. Mellan olika datorer får man byta ut `localhost` mot datorns lokala IP-adress. **OBS!** Det verkar inte fungera så bra mellan flera datorer på chalmers nätverk. Det fungerar dock fint mellan flera datorer i ett hemnätverk. @@ -17,10 +17,10 @@ för att ansluta. Mellan olika datorer får man byta ut `localhost` mot datorns 2. Ange nätverksporten som argument till programmet (eller slumpa fram den) och låt även användaren välja vilken port programmet ska ansluta till. (**Svårighetsgrad 2**) 3. Multitrådning: (**Svårighetsgrad 3**) 1. Som ni märker finns vissa begränsningar med att använda endast en socket - programmet låser sig då det väntar på ett meddelande. För att undvika detta behöver det finnas två trådar som körs parallellt: en som lyssnar efter användarens input och en som lyssnar efter meddelanden på nätverksporten. Skapa därför två olika funktioner som kör varsin loop. Användaren ska kunna avbryta programmet när som helst genom att skriva något lämpligt ord. - 2. Skapa en ny tråd mha [Process](https://docs.python.org/2/library/multiprocessing.html#multiprocessing.Process). Funktionen för att läsa meddelanden, `f`, används som ett argument till `Process` konstruktor: + 2. Skapa en ny tråd mha [Process](https://docs.python.org/3.7/library/multiprocessing.html#multiprocessing.Process). Funktionen för att läsa meddelanden, `f`, används som ett argument till `Process` konstruktor: `p = Process(target=f, args=())`. 3. Tyvärr så är inte sockets trådsäkra, därför behöver det skapas en ny context och en ny socket i den nya tråden. Om samma socket eller context används i två trådar får man en `ZMQError: Interrupted system call`. - Alltså, huvudtråden har en socket som gjort `bind()` och som i en loop läser in `raw_input()` och skickar iväg detta med `send()`. Den andra tråden har en annan socket som gjort `connect()` och som i en egen loop skriver ut värdet av `recv()`. + Alltså, huvudtråden har en socket som gjort `bind()` och som i en loop läser in `input()` och skickar iväg detta med `send()`. Den andra tråden har en annan socket som gjort `connect()` och som i en egen loop skriver ut värdet av `recv()`. 4. Låt den nya tråden vara en s.k. *daemon*; det innebär att den garanterat avslutas då huvudtråden avslutas. Efter dessa justeringar ska man då kunna ansluta mellan två terminalfönster på samma dator samt skicka och ta emot meddelanden som man förväntar sig i ett chat-program. 4. Låt nu användaren skriva in adress och port till den dator som programmet ska ansluta till. Använd fortfarande `"localhost"` som default, men man ska nu kunna ansluta mellan två olika datorer över ett lokalt nätverk. Datorns lokala IP kan erhållas genom: diff --git a/exercises/chat/online_examples/pub_server.py b/exercises/chat/online_examples/pub_server.py index d320a37..2c024e4 100644 --- a/exercises/chat/online_examples/pub_server.py +++ b/exercises/chat/online_examples/pub_server.py @@ -18,6 +18,6 @@ while True: topic = random.randrange(9999, 10005) messagedata = random.randrange(1, 215) - 80 - print("%d %d" % (topic, messagedata)) - socket.send_string("%d %d" % (topic, messagedata)) + print("{}{}".format(topic, messagedata)) + socket.send_string("{}{}".format(topic, messagedata)) time.sleep(1) diff --git a/exercises/chat/online_examples/sub_client.py b/exercises/chat/online_examples/sub_client.py index cd55a9b..2015b09 100644 --- a/exercises/chat/online_examples/sub_client.py +++ b/exercises/chat/online_examples/sub_client.py @@ -18,10 +18,10 @@ socket = context.socket(zmq.SUB) print("Collecting updates from weather server...") -socket.connect("tcp://localhost:%s" % port) +socket.connect("tcp://localhost:{}".format(port)) if len(sys.argv) > 2: - socket.connect("tcp://localhost:%s" % port1) + socket.connect("tcp://localhost:{}".format(port)) # Subscribe to zipcode, default is NYC, 10001 topicfilter = "10001" @@ -34,5 +34,4 @@ topic, messagedata = string.split() total_value += int(messagedata) print(topic, messagedata) - -print("Average messagedata value for topic '%s' was %dF" % (topicfilter, total_value / update_nbr)) + print("Average messagedata value for topic '{}' was {}F".format(topicfilter, (total_value / update_nbr))) diff --git a/exercises/chat/zmq_chat.py b/exercises/chat/zmq_chat.py index 20e034c..d40de66 100644 --- a/exercises/chat/zmq_chat.py +++ b/exercises/chat/zmq_chat.py @@ -13,7 +13,7 @@ def receive_loop(connect_to, channel): """Connects to a client on this channel. Listens for and prints messages indefinitely.""" local_context = zmq.Context() subscribe = local_context.socket(zmq.SUB) - subscribe.setsockopt(zmq.SUBSCRIBE, channel) + subscribe.setsockopt(zmq.SUBSCRIBE, bytes(channel, 'utf-8')) try: subscribe.connect(connect_to) @@ -48,7 +48,7 @@ def connect(): address = "localhost" try: port = int(input("port: ")) - connect_to = "tcp://%s:%s" % (address, port) + connect_to = "tcp://{}:{}".format(address, port) except: print("## Error! Should look like '192.168.0.1 5556'") return @@ -104,9 +104,9 @@ def io_loop(): elif inp.startswith('\\help'): help() else: - print("## Unrecognized command %s. Type `\\help` to see what commands are available." % inp) + print("## Unrecognized command {}. Type `\\help` to see what commands are available.".format(inp)) else: - tracker = publish.send("%s> %s" % (filter, inp), copy=False, track=True) + tracker = publish.send(bytes("{}> {}".format(filter, inp), 'utf-8'), copy=False, track=True) tracker.wait(5) if not tracker.done: print("## Timeout after 5 sec... :P") @@ -124,11 +124,11 @@ def io_loop(): main_context = zmq.Context() publish = main_context.socket(zmq.PUB) - port = 5556 + port = 5557 if len(sys.argv) > 1: port = sys.argv[1] - publish.bind("tcp://*:%s" % port) + publish.bind("tcp://*:{}".format(port)) print("Local IP:", socket.gethostbyname(socket.gethostname())) print("Port:", port) From 19aad225f8561291a6d40757b237a039c58704a4 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Wed, 27 Mar 2019 13:27:03 +0100 Subject: [PATCH 25/47] Updated readme slightly. --- exercises/battleship/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exercises/battleship/README.md b/exercises/battleship/README.md index 8702d69..cd82a31 100644 --- a/exercises/battleship/README.md +++ b/exercises/battleship/README.md @@ -19,17 +19,17 @@ I denna uppgiften kommer ni att självständigt skapa en implementation av *Batt Följande skall finnas i spelet: - 1. Spelet skall börja med att båda spelarna plaserar sina skepp på sin sida av spelplanen utan att motståndaren kan se skeppen. + 1. Spelet skall börja med att båda spelarna placerar sina skepp på sin sida av spelplanen utan att motståndaren kan se skeppen. - 2. Ett skepp skall kunna roteras under plaseringssekvensen. + 2. Ett skepp skall kunna roteras under placeringssekvensen. 3. När spelaren är klar kan inte dennes skepp flyttas. 4. När båda spelarna har plaserat sina skepp börjar stridsläget. - 5. Varje spelare tar tur om att försöka skjuta på motståndarens skepp, vare sig man träffar en skeppsdel eller inte skall detta markeras, givetvis med olika markeringar. Man kan alltid se sina tidigare markeringar under sin egen tur, men inte under motståndarens. + 5. Varje spelare turas om att försöka skjuta på motståndarens skepp, vare sig man träffar en skeppsdel eller inte skall detta markeras, givetvis med olika markeringar. Man kan alltid se sina tidigare markeringar under sin egen tur, men inte under motståndarens. - 6. När alla en spelares skeppsdelar har sänkts vinner motståndaren och spelet avslutas, alternativt börjar om. + 6. När alla av någon spelares skepsdelar har sänkts vinner motståndaren och spelet avslutas, alternativt börjar om. 7. På sin egen runda kan man se sina egna skepp, som då är markerade på de delar som motståndaren träffat, men motståndarens missade skott syns inte. From e9b6173ab3afd6c7de5e41dafd50fd0f06fe49dd Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sun, 14 Apr 2019 16:08:26 +0200 Subject: [PATCH 26/47] Update pygletMp3player.py --- exercises/mp3player/pygletMp3Player.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/exercises/mp3player/pygletMp3Player.py b/exercises/mp3player/pygletMp3Player.py index d5d8e27..99e4036 100644 --- a/exercises/mp3player/pygletMp3Player.py +++ b/exercises/mp3player/pygletMp3Player.py @@ -10,7 +10,7 @@ # Doesn't work so well with the sample.wav :P # Only supports WAV unless one also installs AVBin -file = 'sample2.wav' +file = 'sample.wav' if len(sys.argv)>1: file = sys.argv[1] @@ -25,14 +25,15 @@ paused = False def help(): - print """\nCommands: + print("""\nCommands: \tEsc or x \t Exit program \tp \t Pause/unpause music -\th \t See this list again""" +\th \t See this list again""") -print """Welcome to this music player! +print("""Welcome to this music player! + +You can give a file as an argument or use the commands below.""") -You can give a file as an argument or use the commands below.""" help() @window.event @@ -43,11 +44,11 @@ def on_key_press(symbol, modifiers): if symbol == key.P: if paused: - print "Resume" + print("Resume") player.play() paused = False else: - print "Pause" + print("Pause") player.pause() paused = True elif symbol == key.R: @@ -67,13 +68,13 @@ def on_key_press(symbol, modifiers): # - need user input to be useful while False: if s == "q" or s == "queue": - file = raw_input("Add file to queue: ") + file = input("Add file to queue: ") music = pyglet.resource.media(file) player.queue(music) elif s == "n" or s == "next": player.next() elif s == "pl" or s == "play": - file = raw_input("Play this file instead: ") + file = input("Play this file instead: ") music = pyglet.resource.media(file) music.play() From d1f85ccea85bbf363be318794c7629951d978bac Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sun, 14 Apr 2019 16:15:44 +0200 Subject: [PATCH 27/47] Updated files to python 3. --- exercises/mp3player/platform_test.py | 4 ++-- exercises/mp3player/pygameMp3.py | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/exercises/mp3player/platform_test.py b/exercises/mp3player/platform_test.py index 05b1e92..b5c0730 100644 --- a/exercises/mp3player/platform_test.py +++ b/exercises/mp3player/platform_test.py @@ -2,6 +2,6 @@ import sys if sys.maxsize > 2**32: - print "using 64 bits" + print("using 64 bits") else: - print "using 32 bits" \ No newline at end of file + print("using 32 bits") \ No newline at end of file diff --git a/exercises/mp3player/pygameMp3.py b/exercises/mp3player/pygameMp3.py index 043cc74..9f880eb 100644 --- a/exercises/mp3player/pygameMp3.py +++ b/exercises/mp3player/pygameMp3.py @@ -10,22 +10,23 @@ pygame.mixer.music.play(0) def help(): - print """\nCommands: + print("""\nCommands: \tend or exit \t Exit program \tr or rewind \t Play from the start \tp or pause \t Pause/unpause music \ts or stop \t Stop playing this one \tpl or play \t Play this file -\th or help \t See this list again""" +\th or help \t See this list again""") #\tq or queue \t Add music to queue -print """Welcome to this music player! -You can give a file as an argument or use the commands below.""" +print("""Welcome to this music player! + +You can give a file as an argument or use the commands below.""") help() # Listen for user input -s=raw_input() +s=input() paused = False while s!="end" and s!="exit": if s == "r" or s == "rewind": @@ -46,7 +47,7 @@ def help(): elif s == "s" or s == "stop": pygame.mixer.music.stop() elif s == "pl" or s == "play": - file = raw_input("Play this file instead: ") + file = input("Play this file instead: ") pygame.mixer.music.load(file) pygame.mixer.music.play(0) elif s == "h" or s == "help": @@ -54,5 +55,5 @@ def help(): elif s == "": pass else: - print "I don't understand what you mean with '"+s+"'. Type `h` or `help` to see a list of commands." - s=raw_input() \ No newline at end of file + print("I don't understand what you mean with '"+s+"'. Type `h` or `help` to see a list of commands.") + s=input() \ No newline at end of file From 2857774e450b5a0d2c20473bfa888771e53de0e4 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sun, 14 Apr 2019 16:20:53 +0200 Subject: [PATCH 28/47] Updated readme. --- exercises/mp3player/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/mp3player/README.md b/exercises/mp3player/README.md index 0b271b6..1fb2b47 100644 --- a/exercises/mp3player/README.md +++ b/exercises/mp3player/README.md @@ -13,7 +13,7 @@ Ett program som spelar upp musik-filer som mp3 och wav. ## Delmoment 1. Importera pygame's musik-bibliotek, [pygame.mixer.music](http://www.pygame.org/docs/ref/music.html). Använd funktionen `load()` för att ladda in en musik-fil. **Svårighetsgrad 1** -2. Vid `play()` så kommer den laddade filen att börja spelas upp. Programmet kommer dock att avslutas innan den hinner spela färdigt (eller ens börja spela). Därför behöver programmet vänta på input från användaren innan den får stängas av. Använd standard-funktionen `raw_input()` för detta. **Svårighetsgrad 1** +2. Vid `play()` så kommer den laddade filen att börja spelas upp. Programmet kommer dock att avslutas innan den hinner spela färdigt (eller ens börja spela). Därför behöver programmet vänta på input från användaren innan den får stängas av. Använd standard-funktionen `input()` för detta. **Svårighetsgrad 1** ## Utbyggnad * Låt användaren välja vilken fil som ska spelas upp genom att ange det som ett argument till programmet: From 15e307729b442de8477f4193a41ef7bbf03c2775 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sun, 14 Apr 2019 16:33:48 +0200 Subject: [PATCH 29/47] Updated palindrome.py to python 3. --- exercises/palindrome/palindrome.py | 140 +++++++++++++++-------------- 1 file changed, 74 insertions(+), 66 deletions(-) diff --git a/exercises/palindrome/palindrome.py b/exercises/palindrome/palindrome.py index 0c2b5fd..cdc3912 100644 --- a/exercises/palindrome/palindrome.py +++ b/exercises/palindrome/palindrome.py @@ -7,83 +7,91 @@ import sys + def is_palindrome(word): - """Returns True if argument is a palindrome, returns False otherwise.""" - word = word.lower().strip() - if word: # Checks that word is not empty (empty sequences are false). - reverse_word = word[::-1] - return word == reverse_word - else: - return False - + """Returns True if argument is a palindrome, returns False otherwise.""" + word = word.lower().strip() + if word: # Checks that word is not empty (empty sequences are false). + reverse_word = word[::-1] + return word == reverse_word + else: + return False + + def clean_string(string): - """Removes non-letters from string leaving only ascii letters (swedish characters removed), - whitespace and hyphens. Returns the clean string. - """ - new_string = "" - string.lower() - for c in string: - if c.isalpha() or c.isspace() or c == '-': - new_string += c - return new_string + """Removes non-letters from string leaving only ascii letters (swedish characters removed), + whitespace and hyphens. Returns the clean string. + """ + new_string = "" + string.lower() + for c in string: + if c.isalpha() or c.isspace() or c == '-': + new_string += c + return new_string + def count_palindromes(text): - """Returns the number of palindromes in a text.""" - count = 0 - text = clean_string(text) - text = text.split() - for word in text: - if is_palindrome(word): - count += 1 - return count + """Returns the number of palindromes in a text.""" + count = 0 + text = clean_string(text) + text = text.split() + for word in text: + if is_palindrome(word): + count += 1 + return count + def palindromes_in_file(path): - """Prints the number of palindromes in file given by path.""" - try: - file = open(path) - data = file.read() - print count_palindromes(data) - file.close() - except IOError as e: - print e.strerror + """Prints the number of palindromes in file given by path.""" + try: + file = open(path) + data = file.read() + print(count_palindromes(data)) + file.close() + except IOError as e: + print(e.strerror) + def interactive_mode(): - """Repeatedly checks if given input from the command line are palindromes.""" - print "Input string to check. (E)xit to exit." - while True: - input = raw_input(": ") - if input == 'E' or input == 'e': - print "Exiting" - exit(0) - else: - input = clean_string(input) - print is_palindrome(input) + """Repeatedly checks if given input from the command line are palindromes.""" + print("Input string to check. (E)xit to exit.") + while True: + inp = input(": ") + if input == 'E' or input == 'e': + print("Exiting") + exit(0) + else: + inp = clean_string(inp) + print(is_palindrome(inp)) + def file_mode(): - """Repeatedly counts the palindromes in file given from the command line.""" - print "Input file name plus extension (file has to be in the same folder as python file)." - print "(E)xit to exit." - while True: - input = raw_input(": ") - if input == 'E' or input == 'e': - print "Exiting" - exit(0) - else: - palindromes_in_file(input) + """Repeatedly counts the palindromes in file given from the command line.""" + print("Input file name plus extension (file has to be in the same folder as python file).") + print("(E)xit to exit.") + while True: + inp = input(": ") + if inp == 'E' or inp == 'e': + print("Exiting") + exit(0) + else: + palindromes_in_file(inp) + def main(): - print "Choose mode: (I)nteractive, (R)ead from file or (E)xit." - while True: - mode = raw_input(": ") - if mode == 'I' or mode == 'i': - interactive_mode() - elif mode == 'R' or mode == 'r': - file_mode() - elif mode == 'E' or mode == 'e': - print "Exiting." - exit(0) - else: - print "Invalid input. (Valid inputs: I, R, E)" + print("Choose mode: (I)nteractive, (R)ead from file or (E)xit.") + while True: + mode = input(": ") + if mode == 'I' or mode == 'i': + interactive_mode() + elif mode == 'R' or mode == 'r': + file_mode() + elif mode == 'E' or mode == 'e': + print("Exiting.") + exit(0) + else: + print("Invalid input. (Valid inputs: I, R, E)") + if __name__ == "__main__": - main() + main() From 5caf46f7f80212d398fad2aa6a313ff14d1796f8 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sun, 14 Apr 2019 16:33:57 +0200 Subject: [PATCH 30/47] Updated readme slightly. --- exercises/palindrome/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/exercises/palindrome/README.md b/exercises/palindrome/README.md index 6903c3e..8e571ec 100644 --- a/exercises/palindrome/README.md +++ b/exercises/palindrome/README.md @@ -1,16 +1,17 @@ # Palindrome -Gör ett program som kan bedöma om ett ord är en palindrom samt räkna antalet palindromer i en textfil. +Gör ett program som kan bedöma om ett ord är en palindrom samt räkna antalet palindrom i en textfil. **Svårighetsgrad:** 2 ## Delmoment 1. Skapa en funktion som givet en sträng bedömer om strängen är en palindrom. **Svårighetsgrad 1.** 2. Gör så att programmet kontinuerligt körs och givet input från terminalen skriver ut om input är en palindrom. **Svårighetsgrad 1.** -3. Gör en funktion som rensar en sträng från onödiga tecken. **Svårighetsgrad 1.** -4. Gör en funktion som givet en längre sträng räknar hur många palindromer som finns i strängen. **Svårighetsgrad 1.** -5. Gör en funktion som läser en textfil och skriver ut hur många palindromer som finns i textfilen. **Svårighetsgrad 2.** +3. Gör en funktion som rensar en sträng från onödiga tecken, alltså t.ex. endast behåller alfabetiska tecken. **Svårighetsgrad 1.** +4. Gör en funktion som givet en längre sträng räknar hur många palindrom som finns i strängen. **Svårighetsgrad 1.** +5. Gör en funktion som läser en textfil och skriver ut hur många palindrom som finns i textfilen. **Svårighetsgrad 2.** 6. Gör så att man kan välja mellan att läsa från terminalen eller en textfil när man startar programmet. **Svårighetsgrad 2.** +Exempelkörning av programmet: ```bash python palindrome.py Choose mode: (I)nteractive, (R)ead from file or (E)xit. From e2dfcbb2f83fc04723d2b03b72a3057ffbe5b748 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sun, 14 Apr 2019 16:46:00 +0200 Subject: [PATCH 31/47] Formatted file. --- .../objects_in_a_bag/objects_in_a_bag.py | 193 +++++++++--------- 1 file changed, 98 insertions(+), 95 deletions(-) diff --git a/exercises/objects_in_a_bag/objects_in_a_bag.py b/exercises/objects_in_a_bag/objects_in_a_bag.py index d8de5cc..df546f2 100644 --- a/exercises/objects_in_a_bag/objects_in_a_bag.py +++ b/exercises/objects_in_a_bag/objects_in_a_bag.py @@ -1,103 +1,106 @@ - from random import shuffle -COLORS = [ "transparent", "red", "green", "blue" ] +COLORS = ["transparent", "red", "green", "blue"] + class Item: - - def __init__( self, name, color, weight ): - self.name = name - self.color = color - self.weight = weight - return - - def getName( self ): - return self.name - - def getColor( self ): - return self.color - - def getWeight( self ): - return self.weight - - def isMyName( self, name ): - return self.name == name - - def getDescription( self ): - return "[name: " + self.getName() + ", color: " + self.getColor() + ", weight: " + str( self.getWeight() ) + "]" + + def __init__(self, name, color, weight): + self.name = name + self.color = color + self.weight = weight + return + + def getName(self): + return self.name + + def getColor(self): + return self.color + + def getWeight(self): + return self.weight + + def isMyName(self, name): + return self.name == name + + def getDescription(self): + return "[name: " + self.getName() + ", color: " + self.getColor() + ", weight: " + str(self.getWeight()) + "]" + class Bag: - - def __init__( self, label ): - self.items = [] - self.label = label - return - - def addItem( self, item ): - self.items.append( item ) - return - - def shuffle( self ): - shuffle( self.items ) - return - - def getItems( self ): - return self.items - - def getLabel( self ): - return self.label - - def getDescription( self ): - total = "'" + self.getLabel() + "' contains the following:\n" - for item in self.getItems(): - total = total + " - " + item.getDescription() + "\n" - return total + + def __init__(self, label): + self.items = [] + self.label = label + return + + def addItem(self, item): + self.items.append(item) + return + + def shuffle(self): + shuffle(self.items) + return + + def getItems(self): + return self.items + + def getLabel(self): + return self.label + + def getDescription(self): + total = "'" + self.getLabel() + "' contains the following:\n" + for item in self.getItems(): + total = total + " - " + item.getDescription() + "\n" + return total + def task_program(): - command = "" - bag = Bag( "Cool bag of stuff" ) - while( True ): - got = input().split( " " ) - l_got = len( got ) - if l_got > 0: - command = got[ 0 ] - if command == "exit": - break - elif command == "add": - if l_got > 4: - print( "Too many arguments!" ) - continue - if l_got == 1: - print( "Too few arguments!" ) - continue - name = "" - color = COLORS[ 0 ] - weight = 0.0 - if l_got > 3: - try: - weight = float( got[ 3 ] ) - except ValueError: - print( "Unable to parse weight." ) - continue - if l_got > 2: - try: - index = COLORS.index( got[ 2 ] ) - color = COLORS[ index ] - except ValueError: - print( "Unable to parse color." ) - continue - name = got[ 1 ] - item = Item( name, color, weight ) - print( "Added item: " + item.getDescription() ) - bag.addItem( item ) - elif command == "show": - print( bag.getDescription() ) - elif command == "shuffle": - bag.shuffle() - print( "Shuffling the bag..." ) - else: - print( "Unknown command: " + command ) - else: - print( "Unable to parse command." ) - -task_program() \ No newline at end of file + command = "" + bag = Bag("Cool bag of stuff") + while (True): + got = input().split(" ") + l_got = len(got) + if l_got > 0: + command = got[0] + if command == "exit": + break + elif command == "add": + if l_got > 4: + print("Too many arguments!") + continue + if l_got == 1: + print("Too few arguments!") + continue + name = "" + color = COLORS[0] + weight = 0.0 + if l_got > 3: + try: + weight = float(got[3]) + except ValueError: + print("Unable to parse weight.") + continue + if l_got > 2: + try: + index = COLORS.index(got[2]) + color = COLORS[index] + except ValueError: + print("Unable to parse color.") + continue + name = got[1] + item = Item(name, color, weight) + print("Added item: " + item.getDescription()) + bag.addItem(item) + elif command == "show": + print(bag.getDescription()) + elif command == "shuffle": + bag.shuffle() + print("Shuffling the bag...") + else: + print("Unknown command: " + command) + else: + print("Unable to parse command.") + + +task_program() From 2a5c3e10e154d1ed67121e2cc2baffac4a59be18 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Wed, 1 May 2019 18:01:10 +0200 Subject: [PATCH 32/47] Updated yatzy. --- exercises/yatzy/README.md | 4 +- exercises/yatzy/dice_graphic.py | 5 +-- exercises/yatzy/scores.py | 69 ++++++++++++++++----------------- exercises/yatzy/yatzy.py | 22 +++++------ 4 files changed, 49 insertions(+), 51 deletions(-) diff --git a/exercises/yatzy/README.md b/exercises/yatzy/README.md index 687f97b..b62ed33 100644 --- a/exercises/yatzy/README.md +++ b/exercises/yatzy/README.md @@ -1,5 +1,5 @@ # Yatzy -Det klassiska tärningsspelet Yatzy med 5 tärningar för `N` spelare. +Det klassiska tärningsspelet [Yatzy](https://en.wikipedia.org/wiki/Yatzy) med 5 tärningar för `N` spelare. - **Svårighetsgrad:** 2 @@ -11,7 +11,7 @@ Det klassiska tärningsspelet Yatzy med 5 tärningar för `N` spelare. ## Utbyggnad * Implementera andra poängregler som kåk, stege etc. **Svårighetsgrad 2**. -* Grafisk ASCII representation av tärningar. **Svårighetsgrad 2**. +* Grafisk representation av tärningar (exempelvis ASCII). **Svårighetsgrad 2**. ## Externa bibliotek * (inga) \ No newline at end of file diff --git a/exercises/yatzy/dice_graphic.py b/exercises/yatzy/dice_graphic.py index 28eed66..e3f9208 100644 --- a/exercises/yatzy/dice_graphic.py +++ b/exercises/yatzy/dice_graphic.py @@ -1,5 +1,4 @@ - dice_graph = {} dice_graph['1_top'] = {} dice_graph['2_mid'] = {} @@ -36,10 +35,10 @@ def print_dice(list): string = "" for ix in list: string += " _____ " + spacing - print string + print(string) for key in dice_graph.values(): string = "" for ix in list: string += key[str(ix)] + spacing - print string + print(string) diff --git a/exercises/yatzy/scores.py b/exercises/yatzy/scores.py index f04bfeb..2c2ab11 100644 --- a/exercises/yatzy/scores.py +++ b/exercises/yatzy/scores.py @@ -1,14 +1,13 @@ - def print_scores(players, score): """Print the entire scoreboard and sum of points""" - #There is probably a much better way of doing this - + # There is probably a much better way of doing this + name_string = " " for player in players: name_string += "\t" + player score[player]['sum'] = 0 - print name_string - + print(name_string) + for key in score_model.keys(): string = key + ":" for player in players: @@ -18,15 +17,16 @@ def print_scores(players, score): else: part_score = 0 string += "\t-" - + score[player]['sum'] += part_score - - print string - + + print(string) + sum_string = "Sum:" for player in players: sum_string += "\t" + str(score[player]['sum']) + def number_points(n, dice): """Rule for ordinary number scores: ex all 5. Returns the `n` number score for these `dice`.""" @@ -34,44 +34,43 @@ def number_points(n, dice): for ix in dice: if ix == n: points += ix - if points/n > 2: - print "Well done, you got " + str(points) + " points" + if points / n > 2: + print("Well done, you got " + str(points) + " points") else: - print "Better luck next time, you got " + str(points) + " points" + print("Better luck next time, you got " + str(points) + " points") return points - -score_model = {\ - '1': (lambda(d): number_points(1, d)),\ - '2': (lambda(d): number_points(2, d)),\ - '3': (lambda(d): number_points(3, d)),\ - '4': (lambda(d): number_points(4, d)),\ - '5': (lambda(d): number_points(5, d)),\ - '6': (lambda(d): number_points(6, d)),\ - 'chans':sum} + + +score_model = { + '1': (lambda d: number_points(1, d)), \ + '2': (lambda d: number_points(2, d)), \ + '3': (lambda d: number_points(3, d)), \ + '4': (lambda d: number_points(4, d)), \ + '5': (lambda d: number_points(5, d)), \ + '6': (lambda d: number_points(6, d)), \ + 'chans': sum} + def points_of(dice, player_score): """Decides which score to use and updates scoreboard. Is called at the end of each player turn. Each score can only be used once per player.""" - - print "Your current scoreboard:" - #Kind of a hack :P, wants to reuse code. Could print entire board but not really relevant. - sub_board = {} - sub_board[" "] = player_score + + print("Your current scoreboard:") + # Kind of a hack :P, wants to reuse code. Could print entire board but not really relevant. + sub_board = {" ": player_score} print_scores(" ", sub_board) - + choice = '-1' while choice not in score_model.keys(): - print "Which score do you want to use?" - - - choice = raw_input("\t").replace(" ", "") + print("Which score do you want to use?") + + choice = input("\t").replace(" ", "") if choice in player_score: - print "Already used " + choice + print("Already used " + choice) choice = '-1' continue - - #Evaluate function pointer + + # Evaluate function pointer points = score_model[choice](dice) player_score[choice] = points - diff --git a/exercises/yatzy/yatzy.py b/exercises/yatzy/yatzy.py index ec32048..71947ca 100644 --- a/exercises/yatzy/yatzy.py +++ b/exercises/yatzy/yatzy.py @@ -9,7 +9,7 @@ def read_name(ix): """Reads player name from CLI, returns string.""" - return raw_input( "What is your name (Player" + str(ix) + ")?\n\t") + return input( "What is your name (Player" + str(ix) + ")?\n\t") def start(players, score): """Read player names from CLI. Initiate dictionary for each player""" @@ -41,13 +41,13 @@ def play_turn(player_name, score): 2. Choose which dice to keep and which to throw anew 3. Count points """ - print "\n-------------------------" - print "Player "+player_name + print("\n-------------------------") + print("Player "+player_name) used = "" for entry in score[player_name]: used += " " + str(entry) - print "You have already used:" + used + print("You have already used:" + used) t = 3 dice_to_throw = TOTAL_DICE @@ -62,14 +62,14 @@ def play_turn(player_name, score): if t == 0: continue - keep = string_to_digit_list(raw_input("Which dice do you want to keep? (Can keep any or all, type numbers)\n\t")) + keep = string_to_digit_list(input("Which dice do you want to keep? (Can keep any or all, type numbers)\n\t")) check = {} for ix in keep: if str(ix) in check: #attempt cheating - print "Cannot pick the same dice more than once! " - print str(ix) + " has already been picked." + print("Cannot pick the same dice more than once! ") + print(str(ix) + " has already been picked.") continue check[str(ix)] = 0 @@ -79,7 +79,7 @@ def play_turn(player_name, score): if dice_to_throw <= 0: break - print "Finally:" + print("Finally:") dice_graphic.print_dice(dice) scores.points_of(dice, score[player_name]) @@ -88,8 +88,8 @@ def play_turn(player_name, score): def game(): """Starts a new game of Yatzy""" - print "Game: Yatzy!\n" - print "\n-------------------------" + print("Game: Yatzy!\n") + print("\n-------------------------") num_players = 2 if len(sys.argv) > 1: @@ -109,7 +109,7 @@ def game(): play_turn(player, scoreboard) turn = turn - 1 - print "\n-------------------------" + print("\n-------------------------") scores.print_scores(players, scoreboard) # Main From ffec85de6cc21667ec2850cf4d4a591407c90151 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Wed, 1 May 2019 18:39:22 +0200 Subject: [PATCH 33/47] Updated tick-tack-toe. --- exercises/tictactoe/README.md | 30 ++++---- exercises/tictactoe/tictactoe.py | 114 ++++++++++++++++++------------- 2 files changed, 84 insertions(+), 60 deletions(-) diff --git a/exercises/tictactoe/README.md b/exercises/tictactoe/README.md index 5cc83e4..d1b6960 100644 --- a/exercises/tictactoe/README.md +++ b/exercises/tictactoe/README.md @@ -1,30 +1,36 @@ # Tic-Tac-Toe -Spelet Tic-Tac-Toe eller tre-i-rad som många kallar den: -Två spelare som ska turas om att lägga kryss (X) resp. nolla (O) på ett rutnät (3x3). Spelaren som först får tre i rad vinner. -Programmet läser in vilken ruta man vill placera sitt tecken på. +Spelet Tic-Tac-Toe eller tre-i-rad som många kallar det: +Två spelare som ska turas om att lägga kryss (X) resp. nolla (O) på ett (3x3) rutnät . Spelaren som först får tre i rad vinner. +Programmet läser in vilken ruta man vill placera sitt tecken på och lägger därefter spelarens markör på rutan. - **Svårighetsgrad:** 1 ## Delmoment -0. Fördelaktigt att ha gjort control_flow uppgiften men inget krav **Svårighetsgrad 1**. +0. Fördelaktigt att ha gjort control_flow uppgiften men inget krav. +**Svårighetsgrad 1** -1. Skapa ett rutnät för spelet i konsollen och skriv en funktion printGameArea() som visar den **Svårighetsgrad 1**. -Tips: Gör det enkelt, använd lista med 9 element. Testa hårdkoda in tecknen och printa ut. +1. Skapa ett rutnät för spelet i konsollen och skriv en funktion printGameArea() som visar en tom 3x3 spelplan. +**Tips**: Gör det enkelt, använd lista med 9 element. Testa hårdkoda in tecknen och printa ut. +**Svårighetsgrad 1** -2. Skapa en funktion placeSign() som givet en position och tecken sätter tecknet på rätt position i rutnätet **Svårighetsgrad 1**. +2. Skapa en funktion placeSign() som givet en position (1-9) och tecken sätter tecknet på rätt position i rutnätet **Svårighetsgrad 1**. -3. Ha en play() funktion där spellogiken ligger. Byt tur mellan två spelare och använd dig av föregående funktion för att sätta ut tecknen. **Svårighetsgrad 1**. +3. Ha en play() funktion där spellogiken ligger. Byt tur mellan två spelare och använd dig av föregående funktion för att sätta ut tecknen. + **Svårighetsgrad 1**. -4. Modifiera funktionen placeSign() så att den hanterar felaktig position, t.ex utanför rutnätet eller att en position är redan upptagen. **Svårighetsgrad 1**. +4. Modifiera funktionen placeSign() så att den hanterar felaktig position, t.ex utanför rutnätet eller att en position är redan upptagen och istället ber spelaren om en ny position. +**Svårighetsgrad 1**. 5. Skapa en funktion hasThreeInRow() som tar in ett tecken och avgör om man har vunnit. -Tips: Skriv alla möjliga fall för enkelhetens skull. Eller skriv någon algoritm. **Svårighetsgrad 1**. +Tips: Skriv alla möjliga fall för enkelhetens skull. Alternativt skriv en algoritm som gör det automatiskt. +**Svårighetsgrad 1**. -6. Modifiera funktionen play() som nu ska använda sig av hasThreeInRow() dvs man ska kunna vinna. +6. Modifiera funktionen play() som nu ska använda sig av hasThreeInRow() dvs man ska kunna vinna. **Svårighetsgrad 1**. -7. Modifiera funktionen play() så att det kan bli oavjort. **Svårighetsgrad 1**. +7. Modifiera funktionen play() så att det kan bli oavjort. + **Svårighetsgrad 1**. ## Externa bibliotek *(inga) \ No newline at end of file diff --git a/exercises/tictactoe/tictactoe.py b/exercises/tictactoe/tictactoe.py index bba810e..6c1730c 100644 --- a/exercises/tictactoe/tictactoe.py +++ b/exercises/tictactoe/tictactoe.py @@ -1,4 +1,4 @@ -gameArea = ['_'] * 9 #initialize a gamearea with nine underscore characters. Symbolising empty spot +gameArea = ['_'] * 9 # initialize a gamearea with nine underscore characters. Symbolising empty spot playerOneSign = 'X' playerTwoSign = 'O' @@ -6,75 +6,93 @@ players = ["Player1", "Player2"] signs = [playerOneSign, playerTwoSign] -def isBusy(position): #determin if the position is occupied already or out of bounds - if(0 <= position and position < 9): - #not gameArea[position] == '_' works as well - return gameArea[position] == playerOneSign or gameArea[position] == playerTwoSign + +def is_busy(position): # determin if the position is occupied already or out of bounds + if 0 <= position < 9: + # not gameArea[position] == '_' works as well + return gameArea[position] == playerOneSign or gameArea[position] == playerTwoSign return False -def isSignExist(sign, position): - if 0 <= position and position < 9: + +def is_sign_exist(sign, position): + if 0 <= position < 9: return gameArea[position] == sign return False -def checkColumns(sign): - return checkDirection(3, sign) or checkDirection(3, sign, 1) or checkDirection(3, sign, 2) - -def checkRows(sign): - return checkDirection(1, sign, 0) or checkDirection(1, sign, 3) or checkDirection(1, sign, 6) - -def checkDirection(dir, sign, offset = 0): - if(dir == 2):#diagonal righUp to leftDown - return isSignExist(sign,dir) and isSignExist(sign, 2*dir) and isSignExist(sign,3*dir) - elif(dir == 3): #columns - return isSignExist(sign,0+offset) and isSignExist(sign, dir+offset) and isSignExist(sign, 2*dir+offset) - elif(dir == 1): #rows - return isSignExist(sign,offset) and isSignExist(sign, offset+1) and isSignExist(sign, offset+2) - elif(dir == 4):# second diagonal leftUp to rightDown - return isSignExist(sign,0) and isSignExist(sign, dir) and isSignExist(sign, 2*dir) + +def check_columns(sign): + return check_direction(3, sign) or check_direction(3, sign, 1) or check_direction(3, sign, 2) + + +def check_rows(sign): + return check_direction(1, sign, 0) or check_direction(1, sign, 3) or check_direction(1, sign, 6) + + +def check_direction(direction, sign, offset=0): + if direction == 2: # diagonal righUp to leftDown + return is_sign_exist(sign, direction) and \ + is_sign_exist(sign, 2 * direction) and \ + is_sign_exist(sign, 3 * direction) + elif direction == 3: # columns + return is_sign_exist(sign, 0 + offset) and \ + is_sign_exist(sign, direction + offset) and \ + is_sign_exist(sign, 2 * direction + offset) + elif direction == 1: # rows + return is_sign_exist(sign, offset) and \ + is_sign_exist(sign, offset + 1) and \ + is_sign_exist(sign, offset + 2) + elif direction == 4: # second diagonal leftUp to rightDown + return is_sign_exist(sign, 0) and \ + is_sign_exist(sign, direction) and \ + is_sign_exist(sign, 2 * direction) else: return False -def hasThreeInRow(sign): - return checkRows(sign) or checkDirection(2, sign) or checkColumns(sign) or checkDirection(4, sign) -def placeSign(position, sign): - position -= 1 #Compensating 0-indexed list so user can enter 1-9 - if not isBusy(position) and 0 <= position and position < 9: +def has_three_in_row(sign): + return check_rows(sign) or check_direction(2, sign) or check_columns(sign) or check_direction(4, sign) + + +def place_sign(position, sign): + position -= 1 # Compensating 0-indexed list so user can enter 1-9 + if not is_busy(position) and 0 <= position < 9: gameArea[position] = sign return True else: return False -def printGameArea(): - - for num in range(0,9): - if (num % 3 == 2): - print(gameArea[num] + "\n"), +def print_game_area(): + board_text = "" + for num in range(0, 9): + board_text += str(gameArea[num]) + if num % 3 == 2: + board_text += "\n" else: - print(gameArea[num] + " "), - print("\n") + board_text += " " + print(board_text + "\n") return + def play(): count = 0 while True: - print("%s's turn") % players[count%2] - desiredPos = int(raw_input("Please enter where you want to place your sign ")) - while True: #simulating do-while loop - if(placeSign(desiredPos, signs[count%2])): - break - desiredPos = int(raw_input("Sorry, you can't place at that position. Choose another ")) - if(hasThreeInRow(signs[count%2])): - print "%s won" % players[count%2] - printGameArea() + print_game_area() + print("{}'s turn".format(players[count % 2])) + desired_pos = int(input("Please enter where you want to place your sign ")) + while True: # simulating do-while loop + if place_sign(desired_pos, signs[count % 2]): + break + desired_pos = int(input("Sorry, you can't place at that position. Choose another ")) + if has_three_in_row(signs[count % 2]): + print("{} won".format(players[count % 2])) + print_game_area() return - if(count >= 8): - print "It's a tie" - printGameArea() + if count >= 8: + print("It's a tie") + print_game_area() return - printGameArea() count += 1 -play() \ No newline at end of file + +play() From 5d1ef5b489f4a94204a25ddb89906199035b9074 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Wed, 8 May 2019 10:38:34 +0200 Subject: [PATCH 34/47] Updated excercise --- exercises/pick_game/README.md | 6 +++--- exercises/pick_game/pick_game.py | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/exercises/pick_game/README.md b/exercises/pick_game/README.md index b0a663d..aa97be1 100644 --- a/exercises/pick_game/README.md +++ b/exercises/pick_game/README.md @@ -7,13 +7,13 @@ Implementera följande enkela spel mellan två spelare: det finns `N` pinnar och ## Delmoment 0. Slumpa antalet pinnar mellan 15 och 25. **Svårighetsgrad 1**. -1. Skapa en loop som växlar tur så länge som det finns pinnar kvar. **Svårighetsgrad 1**. -2. I varje iteration: skriv ut antalet pinnar som finns kvar och låt spelaren välja att avlägsna 1 eller 2 pinnar. **Svårighetsgrad 1**. +1. Skapa en loop som växlar tur mellan spelarna så länge som det finns pinnar kvar. **Svårighetsgrad 1**. +2. I varje iteration: skriv ut antalet pinnar som finns kvar och låt den aktuella spelaren välja att avlägsna 1 eller 2 pinnar. **Svårighetsgrad 1**. 3. Spelaren som tar den sista pinnen vinner. **Svårighetsgrad 1**. ## Utbyggnad * Låt varje spelare välja varsitt namn innan matchen startar. **Svårighetsgrad 1**. -* Skriv ut grafisk representation av antalet pinnar istället för siffra: `"||||| ||||| ||"`. **Svårighetsgrad 1**. +* Skriv ut grafisk representation av antalet pinnar istället för siffra, exempelvis: `"||||| ||||| ||"` istället för 12. **Svårighetsgrad 1**. * Låt spelaren välja att spela 1vs1 eller 1vsAI, där AI antingen är väldigt intelligent, helt slumpad eller något därimellan. **Svårighetsgrad 2**. ## Externa bibliotek diff --git a/exercises/pick_game/pick_game.py b/exercises/pick_game/pick_game.py index da03c62..7fba9c2 100644 --- a/exercises/pick_game/pick_game.py +++ b/exercises/pick_game/pick_game.py @@ -11,7 +11,7 @@ def read_name(ix): """Reads player name from CLI, returns string.""" - return raw_input( "What is your name (Player" + str(ix) + ")?\n\t") + return input( "What is your name (Player" + str(ix) + ")?\n\t") def start(multiplayer): """Sets player names by input or AI, depending on multiplayer or not.""" @@ -31,7 +31,7 @@ def print_sticks(sticks): if (ix%5 == 0): string += " " ix = ix + 1 - print string + print(string) def play_turn(player_name, sticks): """Returns 1 or 2, @@ -39,9 +39,9 @@ def play_turn(player_name, sticks): choice = -1 while choice != 1 and choice != 2: print_sticks(sticks) - choice = int(raw_input(player_name + ": pick 1 or 2 sticks?\n\t")) + choice = int(input(player_name + ": pick 1 or 2 sticks?\n\t")) if choice !=1 and choice != 2: - print "Can only pick 1 or 2, not "+str(choice) + print("Can only pick 1 or 2, not "+str(choice)) return choice @@ -66,15 +66,15 @@ def ai_turn(sticks): string += "s." else: string += "." - print string + print(string) return choice def game(): """Starts a new game of Pick one pick two. Makes a 1vsAI game if argument is 'ai', otherwise 1v1.""" - print "Game: Pick one pick two!\n" - print "\n-------------------------" + print("Game: Pick one pick two!\n") + print("\n-------------------------") multiplayer = True if len(sys.argv) > 1 and sys.argv[1]=="ai": @@ -93,8 +93,8 @@ def game(): sticks -= play_turn( last_player, sticks ) turn = turn + 1 - print "\n-------------------------" - print last_player + " won!" + print("\n-------------------------") + print(last_player + " won!") # Main game() \ No newline at end of file From e22fcf29be09b4fe8025ffc719337f28170c58ec Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 11 May 2019 12:41:51 +0200 Subject: [PATCH 35/47] Plot 2d now works on python 3. --- exercises/plot_2d/README.md | 12 +- exercises/plot_2d/multiplot_2d.py | 35 +++--- exercises/plot_2d/plot_2d.py | 183 ++++++++++++++++-------------- 3 files changed, 119 insertions(+), 111 deletions(-) diff --git a/exercises/plot_2d/README.md b/exercises/plot_2d/README.md index 1de9333..c721f2c 100644 --- a/exercises/plot_2d/README.md +++ b/exercises/plot_2d/README.md @@ -1,17 +1,17 @@ #Plot 2d -Skriv en grafritare som kan rita en funktion `y = f(x)` i två dimensioner över ett givet intervall. Till exempel, rita `3*sin(x)-x` över `0 `[0,480]`. **Svårighetsgrad 2**. -3. I omvandlingsfunktionen: ta nu även hänsyn till skala - gör så att fönstret utgör området `0 `[320, 480]`. **Svårighetsgrad 2**. -4. Skapa en funktion som omvandlar en matematisk funktion `f` till `N` koordinater `[x, y]` med jämna mellanrum. Till exempel `f(x)=2*x` -> `[[0,0],[1,2],...,[5,10]]` **Svårighetsgrad 3**. -5. Omvandla de `N` koordinaterna till en lista av pixel-positioner. **Svårighetsgrad 2**. -6. Rita ut funktionen genom att stoppa in listan av positioner till `pygame.draw.lines(...)`. **Svårighetsgrad 2**. +2. Antag ett koordinatsystem med y positivt uppåt och x positivt åt höger, dvs som man brukar rita dem i matten. Skapa en funktion för att omvandla en koordinat i detta system till en "pixel-position" i fönstret (skillnaden är att y är positivt nedåt istället för uppåt). Denna kan lätt kontrolleras mha att rita linjer enligt 1. Till exempel `[0,0]` -> `[0,480]`. **Svårighetsgrad 2**. +4. I omvandlingsfunktionen: ta nu även hänsyn till skala - gör så att fönstret utgör området `0 < x < 10` och `0 < y < 10` snarare än `0 < x < 640` etc. Exempelvis om funktionen får in `[5,0]` returneras `[320, 480]`. **Svårighetsgrad 2**. +5. Skapa en funktion som omvandlar en matematisk funktion `f` till `N` koordinater `[x, y]` med jämna mellanrum. Till exempel `f(x)=2*x` -> `[[0,0],[1,2],...,[5,10]]` **Svårighetsgrad 3**. +6. Omvandla de `N` koordinaterna till en lista av pixel-positioner. **Svårighetsgrad 2**. +7. Rita ut funktionen genom att stoppa skicka listan av positioner till `pygame.draw.lines(...)`. **Svårighetsgrad 2**. ## Utbyggnad - Anpassa så att fönstret går från `x_min < x < x_max` istället för `0 < x < 10`. Observera att `x_min` respektive `y_min` såväl som `x_max` och `y_max` kan vara negativa. **Svårighetsgrad 3**. diff --git a/exercises/plot_2d/multiplot_2d.py b/exercises/plot_2d/multiplot_2d.py index 70d85b8..47d0ae8 100644 --- a/exercises/plot_2d/multiplot_2d.py +++ b/exercises/plot_2d/multiplot_2d.py @@ -1,4 +1,3 @@ - import math, pygame, plot_2d from pygame.locals import * @@ -6,12 +5,13 @@ ############################################# ## Standard colors (RGB) -BLACK = ( 20, 20, 40) +BLACK = (20, 20, 40) WHITE = (255, 255, 255) -BLUE = ( 20, 20, 255) -GREEN = ( 0, 255, 0) -RED = (255, 0, 0) -CYAN = ( 50, 255, 255) +BLUE = (20, 20, 255) +GREEN = (0, 255, 0) +RED = (255, 0, 0) +CYAN = (50, 255, 255) + ############################################# ## Customize plot here @@ -19,17 +19,17 @@ def functions_to_print(): """Write list of functions to plot here of the format [f_info1, f_info2, ...], where f_info == (f, color). Each function `f` must take a single number x and return a single number y.""" - return [\ - (lambda(x): -x*(x-3), GREEN),\ - (lambda(x): 2*math.cos(x)-0.5*x, CYAN) \ - ] + return [ + (lambda x: -x * (x - 3), GREEN), + (lambda x: 2 * math.cos(x) - 0.5 * x, CYAN)] + -#Imports window settings from plot_2d.py +# Imports window settings from plot_2d.py ############################################# ## Let the program calculate the rest -def plot_fun( f_info, X_MIN, X_MAX, N_POINTS, screen): +def plot_fun(f_info, X_MIN, X_MAX, N_POINTS, screen): """Plots a function `f` with the specified settings. f_info == (f, color)""" @@ -37,20 +37,20 @@ def plot_fun( f_info, X_MIN, X_MAX, N_POINTS, screen): pp = map(plot_2d.coordinate_to_position, cc) plot_2d.draw(screen, pp, f_info[1]) + def main(): """Graphics: draws graphs on window and await EXIT or ESCAPE.""" pygame.init() screen = pygame.display.set_mode([plot_2d.WIDTH, plot_2d.HEIGHT]) pygame.display.set_caption('Multiplot 2d') - + clock = pygame.time.Clock() screen.fill(plot_2d.background_color) - - map(lambda(f): plot_fun(f, plot_2d.X_MIN, plot_2d.X_MAX, plot_2d.N_POINTS, screen),\ - functions_to_print()) + + map(lambda f: plot_fun(f, plot_2d.X_MIN, plot_2d.X_MAX, plot_2d.N_POINTS, screen), functions_to_print()) plot_2d.draw_axis(screen) - + pygame.display.update() while 1: e = pygame.event.wait() @@ -59,6 +59,7 @@ def main(): pygame.quit() + # if Python says run... if __name__ == '__main__': main() diff --git a/exercises/plot_2d/plot_2d.py b/exercises/plot_2d/plot_2d.py index a994a58..8b94843 100644 --- a/exercises/plot_2d/plot_2d.py +++ b/exercises/plot_2d/plot_2d.py @@ -1,14 +1,14 @@ - import math, pygame from pygame.locals import * ############################################# ## Standard colors (RGB) -BLACK = ( 20, 20, 40) +BLACK = (20, 20, 40) WHITE = (255, 255, 255) -BLUE = ( 0, 0, 255) -GREEN = ( 0, 255, 0) -RED = (255, 0, 0) +BLUE = (0, 0, 255) +GREEN = (0, 255, 0) +RED = (255, 0, 0) + ############################################# ## Customize plot here @@ -16,22 +16,23 @@ def function_to_print(x): """Write function to plot here. Must take a single number x and return a single number y.""" - return -x*(x-3) + return -x * (x - 3) -#Range of window + +# Range of window X_MIN = 0.0 X_MAX = 10.0 Y_MIN = -10.0 Y_MAX = 10.0 -#Tick interval on axes +# Tick interval on axes X_TICK = 2.5 Y_TICK = 2.5 -#Granularity of plotted functions, more points -> higher resolution plot +# Granularity of plotted functions, more points -> higher resolution plot N_POINTS = 100 -#Colors +# Colors background_color = BLACK plot_color = GREEN grid_color = WHITE @@ -45,15 +46,17 @@ def function_to_print(x): WIDTH = 640 HEIGHT = 480 -X_SIZE = X_MAX-X_MIN -Y_SIZE = Y_MAX-Y_MIN +X_SIZE = X_MAX - X_MIN +Y_SIZE = Y_MAX - Y_MIN + def coordinate_to_position(c): """Converts a model coordinate (vector) into a graphic position (pixel)""" - gx = (c[0]-X_MIN)*WIDTH/X_SIZE - gy = HEIGHT - (c[1]-Y_MIN)*HEIGHT/Y_SIZE - return [gx, gy] - + gx = (c[0] - X_MIN) * WIDTH / X_SIZE + gy = HEIGHT - (c[1] - Y_MIN) * HEIGHT / Y_SIZE + return gx, gy + + def curve_coordinates(f, x0, x1, points): """Returns list of coordinates @@ -61,22 +64,25 @@ def curve_coordinates(f, x0, x1, points): Length of returned list == points.""" coordinates = [] x = x0 - delta = (x1-x0)/(points-1) + delta = (x1 - x0) / (points - 1) while x <= x1: coordinates += [[x, f(x)]] x += delta return coordinates + def linspace(x0, x1, points): """Returns a list of numbers of `points` elements, with constant intervals between `x0` and `x1`""" - delta = (x1-x0)/(points-1) - return map(lambda(x): x0 + delta*x, range(points)) + delta = (x1 - x0) / (points - 1) + return map(lambda x: x0 + delta * x, range(points)) + def curve_coordinates2(f, x0, x1, points): """(Alternative implementation): This is more compact and functional-like.""" - return [ [x, f(x)] for x in linspace(x0,x1,points)] + return [[x, f(x)] for x in linspace(x0, x1, points)] + def draw_ticks(screen, axis): """Draws appropriate ticks on the specified axis. @@ -95,57 +101,56 @@ def draw_ticks(screen, axis): max = Y_MAX tick = Y_TICK limit = WIDTH - + start = min + min % tick end = max - max % tick - points = (end-start)/tick + 1 - t = limit/120 - + points = (end - start) / tick + 1 + t = limit / 120 + for x in linspace(start, end, int(points)): - c=[0,0] + c = [0, 0] c[axis] = x v = coordinate_to_position(c) - - a = v[1-axis]+t - if(a > limit): + + a = v[1 - axis] + t + if a > limit: a = limit - - b = v[1-axis]-t - if(b < 0): + + b = v[1 - axis] - t + if b < 0: b = 0 - - #Copying v - s=list(v) - s[1-axis] = a - - e=list(v) - e[1-axis] = b + + # Copying v + s = list(v) + s[1 - axis] = a + + e = list(v) + e[1 - axis] = b pygame.draw.line(screen, grid_color, s, e, 2) - + + def draw_x_ticks(screen): """(Alternative implementation): Draws appropriate ticks on the X-axis.""" start = X_MIN + X_MIN % X_TICK end = X_MAX - X_MAX % X_TICK - points = (end-start)/X_TICK + 1 - - #t == half length of the tick line - t = HEIGHT/120 - - #one iteration per tick + points = (end - start) / X_TICK + 1 + + # t == half length of the tick line + t = HEIGHT / 120 + + # one iteration per tick for x in linspace(start, end, int(points)): v = coordinate_to_position([x, 0]) - a = v[1]+t - b = v[1]-t - - if(a > HEIGHT): + a = v[1] + t + b = v[1] - t + + if a > HEIGHT: a = HEIGHT - if(b < 0): + if b < 0: b = 0 - pygame.draw.line(screen, grid_color,\ - [v[0], a],\ - [v[0], b],\ - 2) + pygame.draw.line(screen, grid_color, [v[0], a], [v[0], b], 2) + def draw_y_ticks(screen): """(Alternative implementation): @@ -153,64 +158,65 @@ def draw_y_ticks(screen): This function mirrors draw_x_ticks(...)""" start = Y_MIN + Y_MIN % Y_TICK end = Y_MAX - Y_MAX % Y_TICK - points = (end-start)/Y_TICK + 1 - - t = WIDTH/120 - + points = (end - start) / Y_TICK + 1 + + t = WIDTH / 120 + for y in linspace(start, end, int(points)): v = coordinate_to_position([0, y]) - #print v - - a = v[0]+t - b = v[0]-t - - if(a > WIDTH): + # print v + + a = v[0] + t + b = v[0] - t + + if (a > WIDTH): a = WIDTH - if(b < 0): + if (b < 0): b = 0 - - pygame.draw.line(screen, grid_color,\ - [a, v[1]],\ - [b, v[1]],\ - 2) + + pygame.draw.line(screen, grid_color, [a, v[1]], [b, v[1]], 2) + def draw(screen, pp, plot_color): """Plots the points `pp` on the specified screen with the specified color.""" - #Function + # Function pygame.draw.lines(screen, plot_color, False, pp, 3) + def draw_axis(screen): """Draws the axes and ticks of the coordinate system.""" ## Alternative implementations: - #draw_x_ticks(screen) - #draw_y_ticks(screen) - + # draw_x_ticks(screen) + # draw_y_ticks(screen) + draw_ticks(screen, 0) draw_ticks(screen, 1) - - #X-Axis - pygame.draw.lines(screen, grid_color, False, map(coordinate_to_position,\ - [[X_MIN, 0],[X_MAX, 0]]), 2) - - #Y-Axis - pygame.draw.lines(screen, grid_color, False, map(coordinate_to_position,\ - [[0, Y_MIN],[0, Y_MAX]]), 2) + + x_points = list(map(coordinate_to_position, [[X_MIN, 0], [X_MAX, 0]])) + y_points = list(map(coordinate_to_position, [[0, Y_MIN], [0, Y_MAX]])) + + # X-Axis + pygame.draw.lines(screen, grid_color, False, x_points, 2) + + # Y-Axis + pygame.draw.lines(screen, grid_color, False, y_points, 2) + def main(): """Graphics: draws graphs on window and await EXIT or ESCAPE.""" pygame.init() screen = pygame.display.set_mode([WIDTH, HEIGHT]) pygame.display.set_caption('Plot 2d') - + clock = pygame.time.Clock() screen.fill(background_color) - + cc = curve_coordinates(function_to_print, X_MIN, X_MAX, N_POINTS) - pp = map(coordinate_to_position, cc) - - #This would typically be done inside the loop, but since it is never - #updated: might as well keep it outside + pp = list(map(coordinate_to_position, cc)) + + # This would typically be done inside the loop, but since it is never + # updated: might as well keep it outside draw(screen, pp, plot_color) draw_axis(screen) @@ -225,6 +231,7 @@ def main(): pygame.quit() + # if Python says run... if __name__ == '__main__': main() From 4d5acfa2eea871a68bd660c1f34c537819939905 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 11 May 2019 12:56:13 +0200 Subject: [PATCH 36/47] Updated printdir to python 3. --- exercises/printdir/README.md | 7 +++---- exercises/printdir/printdir.py | 14 +++++++------- exercises/printdir/printdir2.py | 22 +++++++++++----------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/exercises/printdir/README.md b/exercises/printdir/README.md index 2b1d548..492a29b 100644 --- a/exercises/printdir/README.md +++ b/exercises/printdir/README.md @@ -2,15 +2,14 @@ #Printdir Skriv ett program som skriver ut vad en mapp innehåller och hur mycket plats varje fil tar samt upprepa detta för mappens undermappar. Programmet ska läsa in vilken sökväg som ska undersökas. - -Detta kan till exempel göras rekursivt. Använd os.path för inbyggda hjälp-funktioner. +För denna uppgifts kan det vara bra att använda sig av rekursion. - **Svårighetsgrad:** 1 ## Delmoment 1. Läs in en sökväg till en mapp (path). **Svårighetsgrad 1** -2. Skriv ut vilka filer den innehåller och deras respektive storlek. **Svårighetsgrad 1** +2. Skriv ut vilka filer den innehåller och deras respektive storlek, för detta kan det vara bra att använda sig av [os.path](https://docs.python.org/2/library/os.path.html). **Svårighetsgrad 1** 3. Upprepa för undermappar (subdirectories). **Svårighetsgrad 1** ## Utbyggnad @@ -18,7 +17,7 @@ Detta kan till exempel göras rekursivt. Använd os.path för inbyggda hjälp-fu - Använd string.format(...) för att formattera utskriften så att det blir fina kolumner. **Svårighetsgrad 1** - Efter mappens namn: skriv ut respektive mapps samlade storlek (summa av innehåll). **Svårighetsgrad 2** - Låt användaren lägga till ett `regex pattern` som filerna och eller mapparna ska följa för att skrivas ut. **Svårighetsgrad 2** -- Lägg till argumentflaggor som -h eller -f etc som gör olika saker. Låt `-h` vara en hjälpfunktion och skriva ut en lista med möjliga flaggor och argument. **Svårighetsgrad 2** +- Lägg till argumentflaggor som -h eller -f etc som gör olika saker. Låt `-h` flaggan skriva ut en lista över alla flaggorna, deras användning samt deras funktion. **Svårighetsgrad 2** - Låt användaren utföra en operation på filerna som att kopiera eller byta namn på dem. *Obs! Testa inte detta på mappar med värdefullt innehåll. Testa alltid på kopior först!* **Svårighetsgrad 2** ## Externa bibliotek diff --git a/exercises/printdir/printdir.py b/exercises/printdir/printdir.py index 47bba3f..84defba 100644 --- a/exercises/printdir/printdir.py +++ b/exercises/printdir/printdir.py @@ -10,19 +10,19 @@ def dir_contents_size(path): Shamelessly copied from https://docs.python.org/2/library/os.html#os.walk""" for root, dirs, files in os.walk(path): - print root, "consumes", - print sum(getsize(join(root, name)) for name in files), - print "bytes in", len(files), "non-directory files" + print(root, "consumes"), + print(sum(getsize(join(root, name)) for name in files)), + print("bytes in", len(files), "non-directory files") def dir_print_contents(path): """Prints contents of a directory recursively. This implementation uses standard functions for iterating.""" for root, dirs, files in os.walk(path): - print "" - print root + os.sep + print("") + print(root + os.sep) for file in files: - print "\t"+file+"\t", getsize(join(root, file)), "bytes" + print("\t"+file+"\t", getsize(join(root, file)), "bytes") def main(): """Prints contents of directory with specified path.""" @@ -30,7 +30,7 @@ def main(): if len(sys.argv)>1: path = sys.argv[1] - print "In ", path, ":" + print("In ", path, ":") dir_print_contents(path) main() \ No newline at end of file diff --git a/exercises/printdir/printdir2.py b/exercises/printdir/printdir2.py index 5ccd051..e319238 100644 --- a/exercises/printdir/printdir2.py +++ b/exercises/printdir/printdir2.py @@ -51,7 +51,7 @@ def recurse_print(path, prefix, file_reg, dir_reg): f_path = join(path, f) if isdir(f_path): if not dir_reg.search(f) : - print "Dir regex didn't match", f + print("Dir regex didn't match", f) continue (string, [f_size, sub_list]) = recurse_print(f_path, prefix+" ", file_reg, dir_reg) @@ -61,7 +61,7 @@ def recurse_print(path, prefix, file_reg, dir_reg): complete_string += prefix+string else : if not file_reg.search(f) : - print "File regex didn't match", f + print("File regex didn't match", f) continue f_size = file_size(path, f) complete_list.append((f, f_size)) @@ -76,8 +76,8 @@ def dir_print_contents(path, file_reg, dir_reg): This implementation uses function recursion.""" (string, [sumsize, sub_list]) = recurse_print(path, "", file_reg, dir_reg) - print "\n"+str_format(path + os.path.sep, sumsize) - print string + print("\n"+str_format(path + os.path.sep, sumsize)) + print(string) def experiment(): """Just for experimenting on regex.""" @@ -86,15 +86,15 @@ def experiment(): for s in strings: if reg.search(s): - print "Matches", s + print("Matches", s) else: - print "No match", s + print("No match", s) def help_format(option, text): return "\n\t{0:<15}{1}".format(option, text) def print_help(): - print """Printdir2 + print("""Printdir2 This is a program for printing of files and calculating the sizes of directories. Is called by typing `python printdir2.py [path] [options]`. @@ -102,7 +102,7 @@ def print_help(): Options:""" +\ help_format("-h", "Print this help and abort.") +\ help_format("-f ", "Only consider files matching this regex.") +\ - help_format("-r ", "Only consider directories matching this regex.") + help_format("-r ", "Only consider directories matching this regex.")) def main(): @@ -134,10 +134,10 @@ def main(): path = sys.argv[ix] ix = ix+1 - print "File reg: " + repr(file_reg) - print "Dir reg: " + repr(dir_reg) + print("File reg: " + repr(file_reg)) + print("Dir reg: " + repr(dir_reg)) - print "\nIn ", path, ":" + print("\nIn ", path, ":") dir_print_contents(path, re.compile(file_reg), re.compile(dir_reg)) main() From 604d02ae5c841716c67d7bae794a913eca75fa50 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 11 May 2019 13:25:23 +0200 Subject: [PATCH 37/47] Updated text-editor --- exercises/text-editor/README.md | 6 +++--- exercises/text-editor/texted.py | 20 +++++++++----------- exercises/text-editor/textedfull.py | 18 ++++++++---------- exercises/text-editor/textedfulloo.py | 18 ++++++++---------- exercises/text-editor/textedmin.py | 10 +++++----- 5 files changed, 33 insertions(+), 39 deletions(-) diff --git a/exercises/text-editor/README.md b/exercises/text-editor/README.md index 1043950..e922b25 100644 --- a/exercises/text-editor/README.md +++ b/exercises/text-editor/README.md @@ -7,9 +7,9 @@ Utveckla en textredigerare i Pythons inbyggda GUI-bibliotek. ## Delmoment 1. Läs igenom, kör och försök förstå texted.py. **Svårighetsgrad 1** -2. Lägg till en Text widget så att man kan skriva text. **Svårighetsgrad 1** -3. Lägg till en Scrollbar widget så att man kan scrolla. **Svårighetsgrad 2** -4. Implementera en oimplementerad funktion i taget och lägg till antingen knappar eller menyknappar så man kan köra funktionerna från programmet. **Svårighetsgrad 2** +2. Lägg till en [Text widget](https://www.tutorialspoint.com/python/tk_text.htm) så att man kan skriva text. **Svårighetsgrad 1** +3. Lägg till en [Scrollbar widget](https://www.tutorialspoint.com/python/tk_scrollbar.htm) så att man kan scrolla. **Svårighetsgrad 2** +4. Gå igenom de icke-implementerade funktionerna och implementera dem en i taget. Lägg till knappar i användargränssnittet som använder sig av dessa funktioner. **Svårighetsgrad 2** 5. Öppna källkoden i er textredigerare och redigera titeln (eller något annat) därifrån. **Svårighetsgrad 1** ## Utbyggnad diff --git a/exercises/text-editor/texted.py b/exercises/text-editor/texted.py index ec1d247..0cf4035 100644 --- a/exercises/text-editor/texted.py +++ b/exercises/text-editor/texted.py @@ -1,19 +1,18 @@ -import Tkinter as Tk -import tkFileDialog +import tkinter as Tk # Text Editor Skeleton def on_new(): # reset path and delete all text in the text box - print "Not implemented" + print("Not implemented") def on_open(): # let user choose what file to open from a dialog (tkFileDialog) # replace text in text box with text from file # handle cancelling of the dialog responsibely - print "Not implemented" + print("Not implemented") def on_save(): @@ -21,40 +20,40 @@ def on_save(): # if the path is already set, save the file using save_file(), otherwise: # let user choose a file to save the content in the text box to (tkFileDialog) # make sure the path is valid (not empty), save the file using save_file() - print "Not implemented" + print("Not implemented") def on_save_as(): # mimic common "save as" behavior # almost the same as on_save(), difference: this always opens a file dialog - print "Not implemented" + print("Not implemented") def get_all_text(): # returns all text in the text box # should be one line of code # not neccessary but may make the code in other places nicer - print "Not implemented" + print("Not implemented") def delete_all_text(): # deletes all text in the text box # should be one line of code # not neccessary but may make the code in other places nicer - print "Not implemented" + print("Not implemented") def save_file(save_path, text): # open file in save_path in write mode # write the text to the file # close the file - print "Not implemented" + print("Not implemented") def read_file(file_path): # open file in file_path # return the text - print "Not implemented" + print("Not implemented") # Initialize application @@ -84,7 +83,6 @@ def read_file(file_path): button = Tk.Button(app, text="Exit", command=quit) button.pack(side=Tk.BOTTOM, fill=Tk.X) - ###################################################### # Start the main event loop (i.e. run the tkinter program) diff --git a/exercises/text-editor/textedfull.py b/exercises/text-editor/textedfull.py index f27032e..ee9e603 100644 --- a/exercises/text-editor/textedfull.py +++ b/exercises/text-editor/textedfull.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -import Tkinter as Tk -import tkFileDialog -import tkSimpleDialog -import tkColorChooser +import tkinter as Tk +from tkinter import filedialog, simpledialog, colorchooser # An implementation with many features without OOP @@ -61,7 +59,7 @@ def on_new(): def on_open(): global path - dialog = tkFileDialog.Open() + dialog = filedialog.Open() new_path = dialog.show() if new_path != '': @@ -76,7 +74,7 @@ def on_save(): new_path = path if new_path == '': - dialog = tkFileDialog.SaveAs(defaultextension='txt') + dialog = filedialog.SaveAs(defaultextension='txt') new_path = dialog.show() if new_path: @@ -87,7 +85,7 @@ def on_save(): def on_save_as(): global path - dialog = tkFileDialog.SaveAs(defaultextension='txt') + dialog = filedialog.SaveAs(defaultextension='txt') new_path = dialog.show() if new_path: @@ -107,7 +105,7 @@ def on_exit(): # EDIT MENU ACTIONS def on_find(): - target = tkSimpleDialog.askstring("Simple Text Editor", "Search for:") + target = simpledialog.askstring("Simple Text Editor", "Search for:") if target: index = text_input.search(target, Tk.INSERT, Tk.END) if not index: @@ -129,13 +127,13 @@ def on_select_all(): # SETTINGS MENU ACTIONS def on_background_color(): - (rgb, hex) = tkColorChooser.askcolor(settings['background_color']) + (rgb, hex) = colorchooser.askcolor(settings['background_color']) settings['background_color'] = hex text_input.config(bg=hex) def on_text_color(): - (rgb, hex) = tkColorChooser.askcolor(settings['text_color']) + (rgb, hex) = colorchooser.askcolor(settings['text_color']) settings['text_color'] = hex text_input.config(fg=hex) diff --git a/exercises/text-editor/textedfulloo.py b/exercises/text-editor/textedfulloo.py index 8ba6886..2e392d1 100644 --- a/exercises/text-editor/textedfulloo.py +++ b/exercises/text-editor/textedfulloo.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -import Tkinter as Tk -import tkFileDialog -import tkSimpleDialog -import tkColorChooser +import tkinter as Tk +from tkinter import filedialog, simpledialog, colorchooser # An implementation with many features using OOP @@ -88,7 +86,7 @@ def on_new(self): self.delete_all_text() def on_open(self): - dialog = tkFileDialog.Open() + dialog = filedialog.Open() path = dialog.show() if path != '': @@ -101,7 +99,7 @@ def on_save(self): path = self.path if path == '': - dialog = tkFileDialog.SaveAs(defaultextension='txt') + dialog = filedialog.SaveAs(defaultextension='txt') path = dialog.show() if path: @@ -110,7 +108,7 @@ def on_save(self): self.save_file(self.path, text) def on_save_as(self): - dialog = tkFileDialog.SaveAs(defaultextension='txt') + dialog = filedialog.SaveAs(defaultextension='txt') path = dialog.show() if path: @@ -129,7 +127,7 @@ def on_exit(self): # EDIT MENU ACTIONS def on_find(self): - target = tkSimpleDialog.askstring("Simple Text Editor", "Search for:") + target = simpledialog.askstring("Simple Text Editor", "Search for:") if target: index = self.text_input.search(target, Tk.INSERT, Tk.END) if not index: @@ -149,12 +147,12 @@ def on_select_all(self): # SETTINGS MENU ACTIONS def on_background_color(self): - (rgb, hex) = tkColorChooser.askcolor(self.settings['background_color']) + (rgb, hex) = colorchooser.askcolor(self.settings['background_color']) self.settings['background_color'] = hex self.text_input.config(bg=hex) def on_text_color(self): - (rgb, hex) = tkColorChooser.askcolor(self.settings['text_color']) + (rgb, hex) = colorchooser.askcolor(self.settings['text_color']) self.settings['text_color'] = hex self.text_input.config(fg=hex) diff --git a/exercises/text-editor/textedmin.py b/exercises/text-editor/textedmin.py index 0de385c..6efb66b 100644 --- a/exercises/text-editor/textedmin.py +++ b/exercises/text-editor/textedmin.py @@ -1,5 +1,5 @@ -import Tkinter as Tk -import tkFileDialog +import tkinter as Tk +from tkinter import filedialog # A minimal implementation, what a solution might look like @@ -12,7 +12,7 @@ def on_new(): def on_open(): global path - dialog = tkFileDialog.Open() + dialog = filedialog.Open() new_path = dialog.show() if new_path != '': @@ -27,7 +27,7 @@ def on_save(): new_path = path if new_path == '': - dialog = tkFileDialog.SaveAs(defaultextension='txt') + dialog = filedialog.SaveAs(defaultextension='txt') new_path = dialog.show() if new_path: @@ -38,7 +38,7 @@ def on_save(): def on_save_as(): global path - dialog = tkFileDialog.SaveAs(defaultextension='txt') + dialog = filedialog.SaveAs(defaultextension='txt') new_path = dialog.show() if new_path: From 18a6b54408e36b29fc979955b6dd004b63b1bb4c Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Wed, 29 May 2019 11:19:10 +0200 Subject: [PATCH 38/47] Program now runs on python 3. --- exercises/robbers_language/robber.py | 125 ++++++++++++--------------- 1 file changed, 53 insertions(+), 72 deletions(-) diff --git a/exercises/robbers_language/robber.py b/exercises/robbers_language/robber.py index 0e42513..efc03a4 100644 --- a/exercises/robbers_language/robber.py +++ b/exercises/robbers_language/robber.py @@ -10,115 +10,96 @@ # Intentionally long source for educational purposes. # -import sys +import mimetypes import os import os.path -import mimetypes +import sys # Helpers CONSONANTS = "qwrtpsdfghjklzxcvbnm" SUFFIX = 'o' + def is_consonant(char): - '''Returns true if char is consonant. False otherwise + """Returns true if char is consonant. False otherwise + """ + return char.lower() in CONSONANTS.lower() - >>> is_consonant('a') - False - >>> is_consonant('d') - True - ''' - return char.lower() in CONSONANTS.lower() def is_valid_file(input): - ''' Returns true if input is a valid text file. - - >>> dir_path = os.path.split(os.path.abspath(__file__))[0] - >>> is_valid_file(dir_path + os.path.sep + 'fixtures' + os.path.sep + 'test.txt') - True - >>> is_valid_file('./bogus') - False - ''' - # Educational moment: Learn how use google to find about - # Python's built in helpers for validating a path for a file - # and checking the file type. - return os.path.isfile(input) and mimetypes.guess_type(input)[0] == "text/plain" + """ Returns true if input is a valid text file. + """ + # Educational moment: Learn how use google to find about + # Python's built in helpers for validating a path for a file + # and checking the file type. + return os.path.isfile(input) and mimetypes.guess_type(input)[0] == "text/plain" + def file_contents_from(path): - ''' Fetch file contents from a file at path. - Returns False if file at path cannot be read. - - >>> dir_path = os.path.split(os.path.abspath(__file__))[0] - >>> file_contents_from(dir_path + os.path.sep + 'fixtures' + os.path.sep + 'test.txt') - 'En enkel fil.\\n' - >>> file_contents_from('./bogus') - False - ''' - try: - f = open(path) - return f.read() - except IOError as e: - return False - else: - f.close() + """ Fetch file contents from a file at path. + Returns False if file at path cannot be read. + """ + try: + f = open(path) + return f.read() + except IOError as e: + return False + # Logic def translate(string): - '''Core translation. + """Core translation. + """ + output = "" - >>> translate('johan') - 'jojohohanon' - ''' - output = "" + for char in string: + output += add_suffix_if_consonant(char) - for char in string: - output += add_suffix_if_consonant(char) + return output - return output -def add_suffix_if_consonant(input): - '''Adds a suffix if input is consonant +def add_suffix_if_consonant(inp): + """Adds a suffix if input is consonant + """ + return inp + SUFFIX + inp.lower() if is_consonant(inp) else inp - >>> add_suffix_if_consonant('j') - 'joj' - >>> add_suffix_if_consonant('a') - 'a' - ''' - return input+SUFFIX+input.lower() if is_consonant(input) else input def cli(): - ''' Interactive CLI. Type 'exit' to quit. - ''' - print 'Type "exit" or press Ctrl+C to leave.' - input = _read_input() + """ Interactive CLI. Type 'exit' to quit. + """ + print('Type "exit" or press Ctrl+C to leave.') + inp = _read_input() - while input != "exit": - print translate(input) - input = _read_input() + while inp != "exit": + print(translate(inp)) + inp = _read_input() + + print("Bye!") - print "Bye!" def _read_input(): - return raw_input("Enter Swedish text: ") + return input("Enter Swedish text: ") + def main(): - if len(sys.argv) == 2 and sys.argv[1] != "": - input = sys.argv[1] + if len(sys.argv) == 2 and sys.argv[1] != "": + inp = sys.argv[1] - out = translate(file_contents_from(input)) if is_valid_file(input) else translate(input) + out = translate(file_contents_from(inp)) if is_valid_file(inp) else translate(inp) - print out + print(out) - else: - cli() + else: + cli() # Init if __name__ == "__main__": - # Docstring unit testing - import doctest - doctest.testmod() + # Docstring unit testing + # import doctest + # doctest.testmod() - main() + main() From 79bc05519f1d81a9ae8ad152d75d9fda488b39df Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Wed, 29 May 2019 11:19:17 +0200 Subject: [PATCH 39/47] Updated readme. --- exercises/robbers_language/README.md | 92 ++++++++++++++-------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/exercises/robbers_language/README.md b/exercises/robbers_language/README.md index b05b3b4..f5d3f21 100644 --- a/exercises/robbers_language/README.md +++ b/exercises/robbers_language/README.md @@ -2,55 +2,55 @@ **Svårighetsgrad:** 1 -Skriv ett program som översätter från svenska till rövarspråket! +Skriv ett program som översätter från svenska till [rövarspråket](http://sv.wikipedia.org/wiki/R%C3%B6varspr%C3%A5ket)! ## Delmoment -1. Ett program som skriver ut översättningen för en given sträng (eller argument under körning). **Svårighetsgrad 1**. - - ```bash - python robber.py Johan - Jojohohanon - ``` - -2. Ett program som väntar på input från användaren och skriver ut översättningen tills programmet avslutas. **Svårighetsgrad 1**. - - ```bash - python robber.py - Hi! What do you want to translate? - > Johan - The translation is: - Jojohohanon - > Hästar är fina. - The translation is: - HoHäsostotaror äror fofinona. - ... - ``` -3. Ett program som givet en sökväg till en textfil översätter allt innehåll. **Svårighetsgrad 1**. - - ```bash - python robber.py svensk_fil.txt - Translating from 'svensk_fil.txt': - - ``` -4. Ett program som gör allt ovanstående på samma gång! Dvs. beroende på argument till programmet ska den bete sig olika. **Svårighetsgrad 2**. - - ```bash - # Med enkelt ord som argument - python robber.py Johan - Jojohohanon - # Inget argument - python robber.py - Hi! What do you want to translate? - > Johan - The translation is: - Jojohohanon - # Sökväg till textfil som argument - python robbery.py svensk_fil.txt - - ``` -Programmet bör alltså känna igen olika argument och därmed bestämma sig för hur den ska köra. - +1. Skriv en metod som översätter en given sträng till rövarspråket **Svårighetsgrad 1** + + Exempel: om funktionen heter `to_robbers_language`: + ```python + print(to_robbers_language("Hejsan")) + ``` + ska till exempel ge följande utskrift i konsollen: + ```bash + HoHejojsosanon + ``` + +2. Lägg nu till så att programmet kan kallas med ett argument från kommandoraden och då skriva ut översättningen av argumentet. + **Svårighetsgrad 1** + + Exempel: om filen heter `robber.py`: + ```bash + python robber.py Hejsan + HoHejojsosanon + ``` + +3. Gör nu så att programmet vid utelämnande av argument istället kontinuerligt frågar användaren efter nya ord att översätta. + **Svårighetsgrad 1** + + ```bash + python robber.py + Hi! What do you want to translate? + > Johan + The translation is: + Jojohohanon + > Hästar är fina. + The translation is: + HoHäsostotaror äror fofinona. + ... + ``` +3. Gör nu så att programmet givet ett argument som är en sökväg till en fil skriver ut översättningen av filens innehåll. Om argumentet inte är en sökväg till en fil ska som tidigare översättningen av argumentet skrivas ut. + **Svårighetsgrad 2** + + Exempel: om skript-filen heter `robber.py` och filen `text_file` innehåller följande text: + `En kort textfil` + ```bash + python robber.py text_file.txt + Translating from 'text_file.txt': + Enon kokorortot totexoxtotfofilol + ``` + ## Om rövarspråket > Rövarspråket (rorövovarorsospoproråkoketot) är ett enkelt kodspråk som framför allt används av barn. Det blev populärt i och med Astrid Lindgrens romaner om Kalle Blomkvist. Idén till rövarspråket ska ha kommit från Astrids make, Sture Lindgren, som använde det i lek med sina kamrater som barn. Eftersom rövarspråket har en enkel struktur som är lätt att avkoda passar sig språket inte i skriven form. Men i talad form kan det vara svårt för en oinsatt att förstå vad som sägs, särskilt om det talas snabbt. From 1f59574f5448023ba5f2da72802a7887225e38f9 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Mon, 10 Jun 2019 13:19:28 +0200 Subject: [PATCH 40/47] Updated survival. --- exercises/survival/README.md | 19 ++-- exercises/survival/game_object.py | 154 +++++++++++++++++------------- exercises/survival/survival.py | 110 ++++++++++++--------- 3 files changed, 157 insertions(+), 126 deletions(-) diff --git a/exercises/survival/README.md b/exercises/survival/README.md index 421ccbd..eaf7bb1 100644 --- a/exercises/survival/README.md +++ b/exercises/survival/README.md @@ -1,6 +1,6 @@ # Survival game -2d shooter spel: överlev mot monster så länge som möjligt. Du har vapen och (o)begränsad ammunition. Spelet kan givetvis anpassas så att det blir precis som man vill. Det här är tänkt att spelas med tangentbord och mus. Perspektivet är tänkt som rakt uppifrån. +2d shooter spel: överlev mot monster så länge som möjligt. Du har vapen och (o)begränsat med ammunition. Spelet kan givetvis anpassas så att det blir precis som man vill. Det här är tänkt att spelas med tangentbord och mus. Perspektivet är tänkt som rakt uppifrån. **Koncept:** Grafik, objekt-orientering, polymorfism @@ -9,23 +9,20 @@ ## Tips **OBS!** Det här är en uppgift av den större och svårare typen. Börja otroligt enkelt först och få det att fungera. Bygg ut det fungerande programmet successivt tills det är så coolt som ni vill ha det. -Exempel kan vara bra för att komma igång, bäst lär man sig dock genom att göra själv. Använd gärna [detta](http://programarcadegames.com/index.php?chapter=introduction_to_sprites) exempel som utgångspunkt. Ni kan även se andra exempel [online](http://www.pygame.org/docs/tut/intro/intro.html) eller i Pygame mappen: -`C:\Python27\Lib\site-packages\pygame\examples`. +Exempel kan vara bra för att komma igång, bäst lär man sig dock genom att göra själv. Använd gärna [detta](http://programarcadegames.com/index.php?chapter=introduction_to_sprites) exempel som utgångspunkt. Ni kan även se andra exempel [online](https://www.pygame.org/tags/all) eller i pygames installationsmap på datorn. -Tänk på att det inte är viktigt hur sprites:en (.png-bilderna) ser ut, rita dem väldigt enkelt i Paint eller motsvarande och lägg istället tiden på att få allt att fungera. När spelet är färdigt kan ni skaffa snygga bilder. - -**OBS!** På Chalmers linux-datorer saknas vissa dependencies till pygame vilket medför att den endast stödjer .bmp-bilder. Detta är relevant att tänka på om ni skulle få felmeddelandet: -`pygame.error: File is not a Windows BMP file`. +Tänk på att det inte är viktigt hur sprites:en (.png-bilderna) ser ut, rita dem väldigt enkelt i Paint/Gimp eller motsvarande och lägg istället tiden på att få allt att fungera. När spelet är färdigt kan ni skaffa snygga bilder. ## Delmoment -0. Skapa ett fönster och en update-loop som ritar om fönstret efter 1/60 sekunder. Varje iteration är en s.k. **frame**. -1. Skapa en egen klass `GraphObject` som [ärver](http://en.wikibooks.org/wiki/A_Beginner's_Python_Tutorial/Classes#Inheritance) `pygame.sprite.Sprite` Skapa en funktion update(self, time) som anropas i varje frame. +0. Skapa ett fönster och en update-loop som ritar om fönstret efter 1/6 +0 sekunder. Varje iteration är en s.k. **frame**. +1. Skapa en egen klass `GraphObject` som [ärver](http://en.wikibooks.org/wiki/A_Beginner's_Python_Tutorial/Classes#Inheritance) av `pygame.sprite.Sprite` Skapa i `GraphObject` en funktion `update(self, time)` som anropas i varje frame. 2. Rita ut några objekt mha `g = pygame.sprite.Group()` och `g.draw(screen)`. -2. Gör en klass för spelaren som ärver `GraphObject` och skapa ett objekt av denna som utgör spelaren. Skapa i denna klass en ny metod `update(self,time)` som även kallar på `GraphObject.update(self,time)`. Detta är ett exempel på [**polymorfism**](http://stackoverflow.com/questions/3724110/practical-example-of-polymorphism). +2. Gör en klass för spelaren som ärver `GraphObject` och skapa ett objekt av denna som utgör spelaren. Skapa i denna klass en ny metod `update(self,time)` som även kallar på `GraphObject.update(self,time)`. Detta är ett exempel på [**polymorfism**](https://sv.wikipedia.org/wiki/Polymorfism_(objektorienterad_programmering). 3. I update-loopen: fånga upp indata genom `pygame.event.get()`. Låt spelaren flytta på sig när man använder pil-tangenterna. Avbryt loopen om `pygame.QUIT` eller Escape matas in. 3. Flytta *kameran* då spelaren rör sig. Detta kan göras på flera sätt, exempelvis genom att man har en Board klass som håller koll på var kameran är. För att se att kameran faktiskt rör sig så är det lämpligt att rita ut några *stillastående* objekt också. Dessa kan senare ersättas med exempelvis bilder av gräs. -4. Skapa en projektil-klass som ärver `GraphObject`. Då du klickar på Space eller vänster musknapp ska en projektil flyga iväg åt det hållet du har muspekaren. +4. Skapa en projektil-klass som ärver `GraphObject`. Då du klickar på Space eller vänster musknapp ska en projektil flyga iväg (från spelaren) åt det hållet du har muspekaren. 4. Skapa en monster-klass som ärver `GraphObject`. Rita ut några monster då spelet startas. 5. Beräkna [kollisioner](http://www.pygame.org/docs/tut/SpriteIntro.html) mha `pygame.sprite.groupcollide()` eller `pygame.sprite.spritecollide()`. Då ett monster träffas av en projektil ska han ta skada och eller dö. Om spelaren kolliderar med ett monster ska spelaren ta skada och eller dö. Se guider ovan under **Tips** för hjälp med kollisionshantering. 6. Skapa nya monster med jämna mellanrum och eller när ett monster dör. Placera ut dem på slumpade positioner. diff --git a/exercises/survival/game_object.py b/exercises/survival/game_object.py index 9073150..6302d61 100644 --- a/exercises/survival/game_object.py +++ b/exercises/survival/game_object.py @@ -1,70 +1,78 @@ - -import pygame, os.path, math, random +import math +import os.path +import pygame +import random main_dir = os.path.split(os.path.abspath(__file__))[0] -resource_path = main_dir+os.path.sep+"resources"+os.path.sep +resource_path = main_dir + os.path.sep + "resources" + os.path.sep + def norm(x, y): """Calculates norm of vector (x, y).""" - return math.sqrt(x**2 + y**2) + return math.sqrt(x ** 2 + y ** 2) + def vector_to(speed, from_x, from_y, target_x, target_y): """Creates a vector of length `speed` in wanted direction.""" x = target_x - from_x y = target_y - from_y - s = speed/norm(x,y) - return (x*s, y*s) + s = speed / norm(x, y) + return x * s, y * s + class Board: """The game world. The camera corner is at the offset: `self.screen_x`""" + def __init__(self, win_width, win_height): self.win_width = win_width self.win_height = win_height self.width = 1200 self.height = 1200 - self.screen_x = (self.width - win_width)/2 - self.screen_y = (self.height - win_height)/2 - - self.limit_x = win_width/2 - self.limit_y = win_height/2 - + self.screen_x = (self.width - win_width) / 2 + self.screen_y = (self.height - win_height) / 2 + + self.limit_x = win_width / 2 + self.limit_y = win_height / 2 + def graph_position_of(self, board_x, board_y): """Pixel position of this board coordinate""" x = board_x - self.screen_x y = board_y - self.screen_y - return (x, y) - + return x, y + def board_position_of(self, graph_x, graph_y): """Board coordinate of this pixel position""" - return (self.screen_x + graph_x, self.screen_y + graph_y) - + return self.screen_x + graph_x, self.screen_y + graph_y + def set_screen_position(self, board_x, board_y): """Adjusts camera to center on `(board_x, board_y)`""" - self.screen_x = board_x - self.win_width/2 - self.screen_y = board_y - self.win_height/2 + self.screen_x = board_x - self.win_width / 2 + self.screen_y = board_y - self.win_height / 2 if self.screen_x < 0: self.screen_x = 0 elif self.screen_x + self.win_width > self.width: self.screen_x = self.width - self.win_width - + if self.screen_y < 0: self.screen_y = 0 elif self.screen_y + self.win_height > self.height: self.screen_y = self.height - self.win_height + class GameOverScreen(pygame.sprite.Sprite): """Game over text sprite.""" def __init__(self): pygame.sprite.Sprite.__init__(self) - self.image = pygame.image.load(resource_path+"gameover.png").convert() + self.image = pygame.image.load(resource_path + "gameover.png").convert() self.rect = self.image.get_rect() - + + class GraphObject(pygame.sprite.Sprite): """Abstract class for any sprite object""" - + def __init__(self, image, board): pygame.sprite.Sprite.__init__(self) self.rect = self.image.get_rect() @@ -73,42 +81,45 @@ def __init__(self, image, board): self.board_x = 0 self.board_y = 0 self.board = board - #self.check() + # self.check() (self.rect.x, self.rect.y) = self.board.graph_position_of(self.board_x, self.board_y) - + def set_board_position(self, board_p): """Moves the sprite to this position""" (self.board_x, self.board_y) = board_p self.update(0) - + def check(self): """Makes sure the object cannot wander off outside the board.""" if self.board_x < 0: self.board_x = 0 elif self.board_x + self.rect.width > self.board.width: self.board_x = self.board.width - self.rect.width - + if self.board_y < 0: self.board_y = 0 elif self.board_y + self.rect.height > self.board.height: self.board_y = self.board.height - self.rect.height - + def update(self, time): """Move and update board and graphical position.""" - self.board_x = (time*self.vx + self.board_x) - self.board_y = (time*self.vy + self.board_y) + self.board_x = (time * self.vx + self.board_x) + self.board_y = (time * self.vy + self.board_y) self.check() (self.rect.x, self.rect.y) = self.board.graph_position_of(self.board_x, self.board_y) - + + class Background(GraphObject): """Background flower. Could be extended to also have grass, etc.""" - image = pygame.image.load(resource_path+"background1.png") - + image = pygame.image.load(resource_path + "background1.png") + def __init__(self, board): GraphObject.__init__(self, SillyMonster.image.convert(), board) + class AbstractMonster(GraphObject): """Abstract class for monsters. Contains common elements such as hitpoints and attack.""" + def __init__(self, image, board, max_hp, speed): GraphObject.__init__(self, image.convert(), board) self.hp = max_hp @@ -118,124 +129,129 @@ def take_damage(self, hp): """Take damage. Return True if died.""" self.hp = self.hp - hp return self.hp <= 0 - + def update(self, time): """Update attack cooldown""" GraphObject.update(self, time) self.attack_wait = self.attack_wait - time - + def attack(self): """Check attack cooldown, returns damage dealt.""" if self.attack_wait <= 0: self.attack_wait = 2 - print "Ouch!" + print("Ouch!") return 1 else: return 0 + class CreepyMonster(AbstractMonster): """A really creepy monster that follows the player around. Looks creepy too.""" - - image = pygame.image.load(resource_path+"monster2.png") + + image = pygame.image.load(resource_path + "monster2.png") speed = 220 - + def __init__(self, board, target): AbstractMonster.__init__(self, CreepyMonster.image, board, 3, 250) self.target = target - + def update(self, time): """Adjust heading to follow the target object.""" AbstractMonster.update(self, time) - (self.vx, self.vy) = vector_to(CreepyMonster.speed, self.board_x, self.board_y, self.target.board_x, self.target.board_y) - + (self.vx, self.vy) = vector_to(CreepyMonster.speed, self.board_x, self.board_y, self.target.board_x, + self.target.board_y) + + class SillyMonster(AbstractMonster): """Silly monster that ignores any other objects. Switches direction at random.""" - - image = pygame.image.load(resource_path+"monster1.png") + + image = pygame.image.load(resource_path + "monster1.png") speed = 150 - + def __init__(self, board): AbstractMonster.__init__(self, SillyMonster.image, board, 5, SillyMonster.speed) - self.countdown = random.uniform(5,7) + self.countdown = random.uniform(5, 7) self.random_decision() - + def update(self, time): """Update switch direction timer.""" AbstractMonster.update(self, time) self.countdown = self.countdown - time if self.countdown < 0: self.random_decision() - self.countdown = random.uniform(5,7) - + self.countdown = random.uniform(5, 7) + def random_decision(self): """Change walking direction""" - (self.vx, self.vy) = vector_to(SillyMonster.speed, 0, 0, random.uniform(-1,1), random.uniform(-1,1)) - + (self.vx, self.vy) = vector_to(SillyMonster.speed, 0, 0, random.uniform(-1, 1), random.uniform(-1, 1)) + + class Character(GraphObject): """Class of the player""" - - image = pygame.image.load(resource_path+"character.png") + + image = pygame.image.load(resource_path + "character.png") speed = 200 - + def __init__(self, board): GraphObject.__init__(self, Character.image.convert(), board) self.horizontal_dir = 0 self.vertical_dir = 0 self.hp = 3 - + def stop(self, xdir, ydir): """Stop moving in specified directions. Is called on arrow key release. `xdir` can take values (-1, 0, 1).""" if self.horizontal_dir == xdir: self.horizontal_dir = 0 - + if self.vertical_dir == ydir: self.vertical_dir = 0 - + self.fix_speed() - + def move(self, xdir, ydir): """Move in specified directions. Is called on arrow key press.""" if xdir != 0: self.horizontal_dir = xdir - - if ydir != 0: + + if ydir != 0: self.vertical_dir = ydir - + self.fix_speed() - + def fix_speed(self): """Adjust speed according to input from arrow keys.""" if self.horizontal_dir == 0 and self.vertical_dir == 0: self.vx = 0 self.vy = 0 - else : + else: (self.vx, self.vy) = vector_to(Character.speed, 0, 0, self.horizontal_dir, self.vertical_dir) - + def take_damage(self, damage): """Was hit by a monster. Take some damage. Returns True if died.""" self.hp = self.hp - damage return self.hp <= 0 - + + class Projectile(GraphObject): """Projectile class.""" - image = pygame.image.load(resource_path+"projectile.png") + image = pygame.image.load(resource_path + "projectile.png") speed = 500 max_time = 2 - + def __init__(self, board): GraphObject.__init__(self, Character.image.convert(), board) self.time_travelled = 0 self.terminated = False - + def set_target(self, target_x, target_y): """Sets target. Will not change direction afterwards.""" (self.vx, self.vy) = vector_to(Projectile.speed, self.rect.x, self.rect.y, target_x, target_y) - + def update(self, time): """Should self-terminate after some time has passed.""" GraphObject.update(self, time) self.time_travelled = self.time_travelled + time if self.time_travelled > Projectile.max_time: - self.terminated = True \ No newline at end of file + self.terminated = True diff --git a/exercises/survival/survival.py b/exercises/survival/survival.py index 364a13a..f7e3a93 100644 --- a/exercises/survival/survival.py +++ b/exercises/survival/survival.py @@ -1,5 +1,6 @@ +import pygame +import random -import pygame, random import game_object # Defining colors @@ -10,8 +11,8 @@ TITLE = "Survival" WIDTH = 640 HEIGHT = 480 -UPDATES_PER_SEC = 60 # Updates per second -TICK_TIME = 0.5 # Seconds per tick +UPDATES_PER_SEC = 60 # Updates per second +TICK_TIME = 0.5 # Seconds per tick # Initiating the window pygame.init() @@ -32,44 +33,48 @@ sprites = pygame.sprite.Group() monsters = pygame.sprite.Group() player_projectiles = pygame.sprite.Group() -#monster_projectiles = pygame.sprite.Group() + + +# monster_projectiles = pygame.sprite.Group() def draw(): """Clears and draws objects to the screen""" global screen global sprites - + screen.fill(WHITE) sprites.draw(screen) pygame.display.flip() + def shoot(): """Player shoots a projectile.""" global main_char global board global sprites - + p = game_object.Projectile(board) - p.set_board_position( (main_char.board_x, main_char.board_y) ) - + p.set_board_position((main_char.board_x, main_char.board_y)) + (tx, ty) = pygame.mouse.get_pos() p.set_target(tx, ty) sprites.add(p) player_projectiles.add(p) + def handle_input(): """Handles the input Arrow keys for moving. Space or LMB for shooting. Escape or QUIT for exiting.""" - + # Global variables that might be changed global running global end_screen global main_char - + for event in pygame.event.get(): if event.type == pygame.QUIT: running = False @@ -100,89 +105,99 @@ def handle_input(): elif event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: shoot() - + + def update(): """Updates the game logic""" global running global sprites global board global main_char - + time = 1.0 / UPDATES_PER_SEC - + board.set_screen_position(main_char.board_x, main_char.board_y) - + for s in sprites: s.update(time) + def populate(constructor): """Function for creating a new sprite object at a random location. - `constructor` is the constructor of the object that only accepts one parameter: `board`. Use lambda expression for more complicated constructors.""" + `constructor` is the constructor of the object that only accepts one parameter: `board`. Use lambda expression + for more complicated constructors. """ global sprites global board - + m = constructor(board) - m.set_board_position(\ - (random.randint(50, board.width-50),\ - random.randint(50, board.height-50))) + m.set_board_position( + (random.randint(50, board.width - 50), + random.randint(50, board.height - 50))) sprites.add(m) return m + def spawn_creepy(): """Spawn a creepy monster with the `main_char` as the target.""" global main_char - m = populate(lambda(b): game_object.CreepyMonster(b, main_char)) + m = populate(lambda b: game_object.CreepyMonster(b, main_char)) monsters.add(m) - + + def spawn_silly(): """Spawn a silly monster""" m = populate(game_object.SillyMonster) monsters.add(m) + def spawn_random_monster(): """Spawn a monster at random. May not spawn any at all (if lucky).""" - c = random.randint(1,100) - if c<60: + c = random.randint(1, 100) + if c < 60: spawn_silly() - elif c<90: + elif c < 90: spawn_creepy() else: - pass + pass + def create_flower(): """Spawn a background flower""" - m = populate(game_object.Background) + populate(game_object.Background) + def game_over(): """Game over function: stops the game and updates flag.""" global running global end_screen - print "GAME OVER!" - + print("GAME OVER!") + running = False end_screen = True + def collisions(): """Resolve all collisions, such as projectile hits.""" global monsters global player_projectiles global sprites - - #Monster hit by player's projectiles + + # Monster hit by player's projectiles for monster in pygame.sprite.groupcollide(monsters, player_projectiles, 0, 1): if monster.take_damage(1): monsters.remove(monster) sprites.remove(monster) spawn_random_monster() spawn_random_monster() - - #Player attacked by monster + + # Player attacked by monster for monster in pygame.sprite.spritecollide(main_char, monsters, 0): if main_char.take_damage(monster.attack()): game_over() return + def remove_terminated_projectiles(): """Remove projectiles that have been travelling for too long.""" global sprites @@ -192,6 +207,7 @@ def remove_terminated_projectiles(): sprites.remove(p) player_projectiles.remove(p) + def init(): """Spawns flowers and start monsters.""" spawn_silly() @@ -200,6 +216,7 @@ def init(): for r in range(8): create_flower() + def main(): """Main function: handles loop and puts everything together.""" # Start the game @@ -207,18 +224,18 @@ def main(): global end_screen running = True end_screen = False - + global sprites global main_char global board - + global WIDTH global HEIGHT sprites.add(main_char) - main_char.set_board_position( board.board_position_of( WIDTH/2, HEIGHT/2) ) - + main_char.set_board_position(board.board_position_of(WIDTH / 2, HEIGHT / 2)) + init() - + ############### ## Game loop while running: @@ -227,34 +244,35 @@ def main(): update() collisions() remove_terminated_projectiles() - + # Draw to the screen draw() - + # Tick... global ticks ticks += 1 clock.tick(UPDATES_PER_SEC) - + # Game over if end_screen: g = game_object.GameOverScreen() - g.rect.x = (WIDTH - g.rect.width)/2 - g.rect.y = (HEIGHT - g.rect.height)/2 - + g.rect.x = (WIDTH - g.rect.width) / 2 + g.rect.y = (HEIGHT - g.rect.height) / 2 + gr = pygame.sprite.Group() gr.add(g) - + global screen sprites.draw(screen) gr.draw(screen) pygame.display.flip() - + while end_screen: handle_input() clock.tick(UPDATES_PER_SEC) pygame.quit() + # Run -main() \ No newline at end of file +main() From c5aa5164b97daaa3ffde98278a1719598bbd28be Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Mon, 10 Jun 2019 14:42:22 +0200 Subject: [PATCH 41/47] Updated tetris. --- exercises/robbers_language/README.md | 22 ++++-- exercises/robbers_language/robber.py | 103 ++++++++++++++------------- exercises/tetris/README.md | 8 +-- 3 files changed, 76 insertions(+), 57 deletions(-) diff --git a/exercises/robbers_language/README.md b/exercises/robbers_language/README.md index b05b3b4..369433a 100644 --- a/exercises/robbers_language/README.md +++ b/exercises/robbers_language/README.md @@ -2,16 +2,28 @@ **Svårighetsgrad:** 1 -Skriv ett program som översätter från svenska till rövarspråket! +Skriv ett program som översätter från svenska till [rövarspråket](https://sv.wikipedia.org/wiki/R%C3%B6varspr%C3%A5ket)! ## Delmoment +1. Skriv en metod som tar emot en sträng och returnerar strängen översatt till rövarspråket. + + Exempelanvändning om metoden heter `translate_to_robber`: + ```python + text = "hejsan" + print(translate_to_robber(text)) + ``` + Ska följande skrivas ut i konsollen + ``` + + ``` + 1. Ett program som skriver ut översättningen för en given sträng (eller argument under körning). **Svårighetsgrad 1**. - ```bash - python robber.py Johan - Jojohohanon - ``` + ```bash + python robber.py Johan + Jojohohanon + ``` 2. Ett program som väntar på input från användaren och skriver ut översättningen tills programmet avslutas. **Svårighetsgrad 1**. diff --git a/exercises/robbers_language/robber.py b/exercises/robbers_language/robber.py index 0e42513..8dba9d5 100644 --- a/exercises/robbers_language/robber.py +++ b/exercises/robbers_language/robber.py @@ -10,42 +10,45 @@ # Intentionally long source for educational purposes. # -import sys +import mimetypes import os import os.path -import mimetypes +import sys # Helpers CONSONANTS = "qwrtpsdfghjklzxcvbnm" SUFFIX = 'o' + def is_consonant(char): - '''Returns true if char is consonant. False otherwise + """Returns true if char is consonant. False otherwise >>> is_consonant('a') False >>> is_consonant('d') True - ''' - return char.lower() in CONSONANTS.lower() + """ + return char.lower() in CONSONANTS.lower() + -def is_valid_file(input): - ''' Returns true if input is a valid text file. +def is_valid_file(inp): + """ Returns true if input is a valid text file. >>> dir_path = os.path.split(os.path.abspath(__file__))[0] >>> is_valid_file(dir_path + os.path.sep + 'fixtures' + os.path.sep + 'test.txt') True >>> is_valid_file('./bogus') False - ''' - # Educational moment: Learn how use google to find about - # Python's built in helpers for validating a path for a file - # and checking the file type. - return os.path.isfile(input) and mimetypes.guess_type(input)[0] == "text/plain" + """ + # Educational moment: Learn how use google to find about + # Python's built in helpers for validating a path for a file + # and checking the file type. + return os.path.isfile(inp) and mimetypes.guess_type(inp)[0] == "text/plain" + def file_contents_from(path): - ''' Fetch file contents from a file at path. + ''' Fetch file contents from a file at path. Returns False if file at path cannot be read. >>> dir_path = os.path.split(os.path.abspath(__file__))[0] @@ -54,71 +57,75 @@ def file_contents_from(path): >>> file_contents_from('./bogus') False ''' - try: - f = open(path) - return f.read() - except IOError as e: - return False - else: - f.close() + try: + f = open(path) + return f.read() + except IOError as e: + return False + # Logic def translate(string): - '''Core translation. + """Core translation. >>> translate('johan') 'jojohohanon' - ''' - output = "" + """ + output = "" + + for char in string: + output += add_suffix_if_consonant(char) - for char in string: - output += add_suffix_if_consonant(char) + return output - return output def add_suffix_if_consonant(input): - '''Adds a suffix if input is consonant + """Adds a suffix if input is consonant >>> add_suffix_if_consonant('j') 'joj' >>> add_suffix_if_consonant('a') 'a' - ''' - return input+SUFFIX+input.lower() if is_consonant(input) else input + """ + return input + SUFFIX + input.lower() if is_consonant(input) else input + def cli(): - ''' Interactive CLI. Type 'exit' to quit. - ''' - print 'Type "exit" or press Ctrl+C to leave.' - input = _read_input() + """ Interactive CLI. Type 'exit' to quit. + """ + print('Type "exit" or press Ctrl+C to leave.') + inp = _read_input() + + while inp != "exit": + print(translate(inp)) + inp = _read_input() - while input != "exit": - print translate(input) - input = _read_input() + print("Bye!") - print "Bye!" def _read_input(): - return raw_input("Enter Swedish text: ") + return input("Enter Swedish text: ") + def main(): - if len(sys.argv) == 2 and sys.argv[1] != "": - input = sys.argv[1] + if len(sys.argv) == 2 and sys.argv[1] != "": + input = sys.argv[1] - out = translate(file_contents_from(input)) if is_valid_file(input) else translate(input) + out = translate(file_contents_from(input)) if is_valid_file(input) else translate(input) - print out + print(out) - else: - cli() + else: + cli() # Init if __name__ == "__main__": - # Docstring unit testing - import doctest - doctest.testmod() + # Docstring unit testing + import doctest + + doctest.testmod() - main() + main() diff --git a/exercises/tetris/README.md b/exercises/tetris/README.md index 8499654..b7cd30e 100644 --- a/exercises/tetris/README.md +++ b/exercises/tetris/README.md @@ -1,19 +1,19 @@ # Tetris - +Det klassiska spelet [Tetris](https://sv.wikipedia.org/wiki/Tetris) implementerat med biblioteket [pygame](http://www.pygame.org/download.shtml). - **Svårighetsgrad:** 3 ## Delmoment -1. Skapa ett fönster att rita upp komponenterna på. +1. Skapa ett pygame fönster att rita upp komponenterna på. 2. Skapa ett simpelt block och få detta att ritas ut. -3. Få blocket att kunna röra sig vid tangenttryckningar. +3. Få blocket att kunna röra sig vid tangenttryckningar (exempelvis vänster/höger piltangent). 4. Gör en spel-loop som flyttar ned blocket med ett visst intervall. 5. Få blocket att, när det når botten av fönstret, ligga kvar samtidigt som ett nytt block börjar falla från toppen. 6. Se till att block inte kan röra sig utanför fönstret eller in i andra block. 7. När en rad är fylld utav block ska den raden tas bort och ovanliggande rader ska flyttas ned. 8. När ett nytt block skapas måste det kontrolleras så att det inte redan finns något block på dess position, om det gör det så har man förlorat och spelet ska avslutas. 9. Om ni inte redan har gjort det så utöka blocket till att bestå av flera block som tillsammans utgör olika former, vilka kan slumpas när ett nytt skapas. Flera ställen i koden behöver uppdateras i detta läge, men det mesta bör vara ganska simpla förändringar (t.ex. flyttar man fyra block samtidigt istället för bara ett). -10. Få block att rotera vid tangenttryckning. +10. Få block att rotera vid tangenttryckning (exempelvis uppåt piltangent). ## Utbyggnad From d4d91997a7d185ab905f67b7f9932a2b73a937c2 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Mon, 10 Jun 2019 14:59:03 +0200 Subject: [PATCH 42/47] Update talbas. --- exercises/talbas/README.md | 2 +- exercises/talbas/convert.py | 8 ++--- exercises/talbas/hex_dec.py | 70 ++++++++++++++++++++----------------- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/exercises/talbas/README.md b/exercises/talbas/README.md index 5035173..f656997 100644 --- a/exercises/talbas/README.md +++ b/exercises/talbas/README.md @@ -7,7 +7,7 @@ Konvertera mellan talbaserna binär och decimal ## Delmoment 1. Skapa en funktion som, givet ett binärt tal i strängform, ger dess decimala motsvarighet. *Svårighetsgrad 1* -2. Skapa en funktion som, givet ett decimalt tal i strängform, ger dess binära motsvarighet. *Svårighetsgrad 1* +3. Skapa en funktion som, givet ett decimalt tal i strängform, ger dess binära motsvarighet. *Svårighetsgrad 1* ## Utbyggnad diff --git a/exercises/talbas/convert.py b/exercises/talbas/convert.py index 6a8b0bd..fa1f6a6 100755 --- a/exercises/talbas/convert.py +++ b/exercises/talbas/convert.py @@ -23,10 +23,10 @@ def bintodec(bin_str): if len(sys.argv) > 2: if sys.argv[1] == 'bin': - print bintodec(sys.argv[2]) + print(bintodec(sys.argv[2])) elif sys.argv[1] == 'dec': - print dectobin(sys.argv[2]) + print(dectobin(sys.argv[2])) else: - print "Usage: convert.py bin|dec number" + print("Usage: convert.py bin|dec number") else: - print "Usage: convert.py bin|dec number" + print("Usage: convert.py bin|dec number") diff --git a/exercises/talbas/hex_dec.py b/exercises/talbas/hex_dec.py index 6b641ef..e7b99af 100644 --- a/exercises/talbas/hex_dec.py +++ b/exercises/talbas/hex_dec.py @@ -1,24 +1,24 @@ - import sys hex_dict = \ - {'0':0,\ - '1':1,\ - '2':2,\ - '3':3,\ - '4':4,\ - '5':5,\ - '6':6,\ - '7':7,\ - '8':8,\ - '9':9,\ - 'a':10,\ - 'b':11,\ - 'c':12,\ - 'd':13,\ - 'e':14,\ - 'f':15\ - } + {'0': 0, + '1': 1, + '2': 2, + '3': 3, + '4': 4, + '5': 5, + '6': 6, + '7': 7, + '8': 8, + '9': 9, + 'a': 10, + 'b': 11, + 'c': 12, + 'd': 13, + 'e': 14, + 'f': 15 + } + def hextodec(hex_str): """Works with both `ff`, `0xff` and upper-case.""" @@ -29,36 +29,42 @@ def hextodec(hex_str): continue num *= 16 num += hex_dict[char] - print str(num) + return str(num) + def hex_char(num): """15->'e' etc. Inverse of hex_dict.""" - if num < 10 and num >= 0: + if 0 < num <= 10: return str(num) elif num < 16: - return chr( ord('a') - 10 + num) + return chr(ord('a') - 10 + num) else: - raise Exception(str(num)+" is not between 0 and 15") + raise Exception(str(num) + " is not between 0 and 15") + def dectohex(dec_str): dec = int(dec_str) hex_str = "" while dec > 0: hex_str = hex_char(dec % 16) + hex_str - dec = dec/16 - print "0x"+hex_str + dec = dec / 16 + return "0x" + hex_str + def dectohex2(dec_str): - "Alternative implementation using BIF" + """Alternative implementation using BIF""" dec = int(dec_str) return hex(dec) + if len(sys.argv) > 2: - if sys.argv[1] == 'hex': - print hextodec(sys.argv[2]) - elif sys.argv[1] == 'dec': - print dectohex2(sys.argv[2]) - else: - print "Usage: convert.py hex|dec number" + if sys.argv[1] == 'hex': + print(hextodec(sys.argv[2])) + elif sys.argv[1] == 'dec': + print(dectohex2(sys.argv[2])) + else: + print("Usage: convert.py hex|dec number") else: - print "Usage: convert.py hex|dec number" \ No newline at end of file + print("Usage: convert.py hex|dec number") + + From f8692a73bf81c23512a219803a01be48aa7d8ada Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Tue, 11 Jun 2019 14:15:06 +0200 Subject: [PATCH 43/47] Updated pyopengl and astar. --- exercises/pyopengl_och_a_star/README.md | 6 +- .../pyopengl_och_a_star.py | 531 +++++++++--------- 2 files changed, 276 insertions(+), 261 deletions(-) diff --git a/exercises/pyopengl_och_a_star/README.md b/exercises/pyopengl_och_a_star/README.md index ad9ba88..c92cfd6 100644 --- a/exercises/pyopengl_och_a_star/README.md +++ b/exercises/pyopengl_och_a_star/README.md @@ -10,8 +10,8 @@ Eftersom att OpenGL bygger på Klient/Server interaktion kommer den delen av upp **Koncept**: Grafik, API, algorithmimplementation, programstrukturering **Externa Bibliotek**: - - Grafik [PyOpenGL](https://pypi.python.org/pypi/PyOpenGL) - - Bilder [PIL](http://www.pythonware.com/products/pil/) + - Grafik [PyOpenGL](http://pyopengl.sourceforge.net/) + - Bilder [Pillow](https://python-pillow.org/) ([Dokumentation](https://pillow.readthedocs.io/en/stable/)) - **Svårighetsgrad:** 3 @@ -52,7 +52,7 @@ Vi behöver ange vad det skapta fönstret skall göra under dess olika tillstån glutIdleFunc( *function()* ) ``` -Funktionen som ges till *glutReshapeFunc* kommer att exekveras då fönstret byter storlek. *glutDisplayFunc* kallas när fönstret behöver ritas om. *glutIdleFunc* genomförs när inget annat exekveras. Det finns många fler tillstånd att binda funktioner till, men dessa kan hittas i [GLUTs egna dokumentation](https://www.opengl.org/resources/libraries/glut/). +Funktionen som ges till *glutReshapeFunc* kommer att exekveras då fönstret byter storlek. *glutDisplayFunc* kallas när fönstret behöver ritas om. *glutIdleFunc* genomförs när inget annat exekveras. Det finns många fler tillstånd att binda funktioner till, men dessa kan hittas i [dokumentationen för freeglut](http://freeglut.sourceforge.net/). Ett tips är att binda renderingsfunctionen även till *glutIdleFunc* för att få konternuerlig uppdatering av fönstret. Går renderingen av någon anledning för fort kan nu läsa om *time* modulens *sleep( secs )* function. diff --git a/exercises/pyopengl_och_a_star/pyopengl_och_a_star.py b/exercises/pyopengl_och_a_star/pyopengl_och_a_star.py index c64fc98..95fdd4d 100644 --- a/exercises/pyopengl_och_a_star/pyopengl_och_a_star.py +++ b/exercises/pyopengl_och_a_star/pyopengl_och_a_star.py @@ -1,11 +1,9 @@ - -from time import * -from math import * from random import * -from PIL import Image -from OpenGL import * +from time import * + from OpenGL.GL import * from OpenGL.GLUT import * +from PIL import Image ## ## Rendering with OpenGL @@ -15,278 +13,295 @@ NODES = None NODE_PATH = None + def task0_initOpenGL(): - glutInit( "" ) - glutInitWindowSize( 480, 480 ) - glutInitWindowPosition( 0, 0 ) - window = glutCreateWindow( b"Python OpenGL" ) - glutReshapeFunc( task0_reshape ) - glutKeyboardUpFunc( task0_keyboard ) - glutDisplayFunc( task0_render ) - glutIdleFunc( task0_render ) - task0_initProgram() - glutMainLoop() - task0_terminateProgram() - -def task0_reshape( win_wid, win_hei ): - glViewport( 0, 0, win_wid, win_hei ) - glMatrixMode( GL_PROJECTION ) - glLoadIdentity() - glOrtho( 0.0, win_wid, 0.0, win_hei, 0.0, 1.0 ) - glMatrixMode( GL_MODELVIEW ) - -def task0_keyboard( a, b, c ): - task2_rebuildNodes() + glutInit("") + glutInitWindowSize(480, 480) + glutInitWindowPosition(0, 0) + window = glutCreateWindow(b"Python OpenGL") + glutReshapeFunc(task0_reshape) + glutKeyboardUpFunc(task0_keyboard) + glutDisplayFunc(task0_render) + glutIdleFunc(task0_render) + task0_initProgram() + glutMainLoop() + task0_terminateProgram() + + +def task0_reshape(win_wid, win_hei): + glViewport(0, 0, win_wid, win_hei) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + glOrtho(0.0, win_wid, 0.0, win_hei, 0.0, 1.0) + glMatrixMode(GL_MODELVIEW) + + +def task0_keyboard(a, b, c): + task2_rebuildNodes() + def task0_render(): - global NODES - global NODE_PATH - glClear( GL_COLOR_BUFFER_BIT ) - glLoadIdentity() - - glEnable( GL_TEXTURE_2D ) - glColor3f( 1.0, 1.0, 1.0 ) - glBegin( GL_QUADS ) - glTexCoord2f( 0.0, 0.0 ) - glVertex2f( 0.0, 480.0 ) - glTexCoord2f( 10.0, 0.0 ) - glVertex2f( 480.0, 480.0 ) - glTexCoord2f( 10.0, 10.0 ) - glVertex2f( 480.0, 0.0 ) - glTexCoord2f( 0.0, 10.0 ) - glVertex2f( 0.0, 0.0 ) - glEnd() - - #This is for task2 - glDisable( GL_TEXTURE_2D ) - task2_renderNodes( NODES, 10, 10 ) - task2_renderNodePath( NODE_PATH, 10, 10 ) - - glutSwapBuffers() - sleep( 1.0 ) - -def task0_loadTexture( image_path ): - texture = glGenTextures( 1 ) - if texture is not 0: - image = Image.open( image_path ) - image_width = image.size[ 0 ] - image_height = image.size[ 1 ] - image_data = image.tostring( "raw", "RGBX", 0, -1 ) - glActiveTexture( GL_TEXTURE0 ) - glBindTexture( GL_TEXTURE_2D, texture ) - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ) - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ) - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ) - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ) - glTexImage2D( GL_TEXTURE_2D, 0, 3, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data ) - return texture + global NODES + global NODE_PATH + glClear(GL_COLOR_BUFFER_BIT) + glLoadIdentity() + + glEnable(GL_TEXTURE_2D) + glColor3f(1.0, 1.0, 1.0) + glBegin(GL_QUADS) + glTexCoord2f(0.0, 0.0) + glVertex2f(0.0, 480.0) + glTexCoord2f(10.0, 0.0) + glVertex2f(480.0, 480.0) + glTexCoord2f(10.0, 10.0) + glVertex2f(480.0, 0.0) + glTexCoord2f(0.0, 10.0) + glVertex2f(0.0, 0.0) + glEnd() + + # This is for task2 + glDisable(GL_TEXTURE_2D) + task2_renderNodes(NODES, 10, 10) + task2_renderNodePath(NODE_PATH, 10, 10) + + glutSwapBuffers() + sleep(1.0) + + +def task0_loadTexture(image_path): + texture = glGenTextures(1) + if texture is not 0: + image = Image.open(image_path) + image_width = image.size[0] + image_height = image.size[1] + image_data = image.tobytes("raw", "RGBX", 0, -1) + glActiveTexture(GL_TEXTURE0) + glBindTexture(GL_TEXTURE_2D, texture) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) + glTexImage2D(GL_TEXTURE_2D, 0, 3, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data) + return texture + def task0_initProgram(): - global TEXTURE - global NODES - global NODE_PATH - glLineWidth( 8.0 ) - glPointSize( 16.0 ) - TEXTURE = task0_loadTexture( "tile.png" ) - task2_rebuildNodes() + global TEXTURE + global NODES + global NODE_PATH + glLineWidth(8.0) + glPointSize(16.0) + TEXTURE = task0_loadTexture("tile.png") + task2_rebuildNodes() + def task0_terminateProgram(): - global TEXTURE - glDeleteTexture( TEXTURE ) + global TEXTURE + glDeleteTexture(TEXTURE) -## + +## ## Path finding with A* ## class Node: - - def __init__( self, posx, posy ): - self.position = ( posx, posy ) - self.cost_g = 0 - self.cost_h = 0 - self.cost_f = 0 - self.opened = False - self.closed = False - self.enabled = True - self.parent = None - self.edges = [] - - def addEdgeNode( self, node ): - self.edges.append( node ) - - def getEdges( self ): - return self.edges - - def getGCostTo( self, node ): - cost = 0 - if node is not None: - cost = cost + node.cost_g - if ( self.position[ 0 ] is node.position[ 0 ] - or self.position[ 1 ] is node.position[ 1 ] ): - cost += 10 - else: - cost += 14 - return cost - def getHCostTo( self, node ): - cost = ( abs( self.position[ 0 ] - node.position[ 0 ] ) - + abs( self.position[ 1 ] - node.position[ 1 ] ) ) * 10 - return cost - def applyCostsToSelf( self, end_node ): - self.cost_g = self.getGCostTo( self.parent ) - self.cost_h = self.getHCostTo( end_node ) - def getFCost( self ): - return self.cost_g + self.cost_h - -def task1_AStarSearch( start_node, end_node ): - if start_node is None: - print( "Missing a starting node." ) - return None - elif end_node is None: - print( "Missing an end node." ) - return None - elif start_node.position is end_node.position: - print( "Already there." ) - return [] - elif not start_node.enabled: - print( "Start node blocked." ) - return None - elif not end_node.enabled: - print( "End node blocked." ) - return None - else: - current = start_node - opened = [ start_node ] - closed = [] - start_node.applyCostsToSelf( end_node ) - start_node.opened = True - number_of_iterations = 0 - while ( number_of_iterations < 200 - and current is not None ): - number_of_iterations += 1 - open_cost = 99999999999 - current = None - for node in opened: - if node.getFCost() < open_cost: - current = node - open_cost = node.getFCost() - if current is end_node: - break - elif current not in opened: - print( "Failed, no path available." ) - return None - else: - opened.remove( current ) - closed.append( current ) - current.closed = True - current.opened = False - for node in current.edges: - if not node.enabled or node.closed: - continue - elif node.opened: - if node.cost_g <= node.getGCostTo( current ): - continue - else: - opened.append( node ) - node.opened = True - node.parent = current - node.applyCostsToSelf( end_node ) - for node in opened: - node.opened = False - for node in closed: - node.closed = False - path = [] - while current is not None: - path.append( current ) - current = current.parent - result = list( reversed( path ) ) - print( "The path consists of " + str( len( result ) ) + " nodes." ) - return result - -def task1_buildRandomNodeField( width, height ): - nodes = [] - for x in range( 0, width ): - l = [] - nodes.append( l ) - for y in range( 0, height ): - l.append( Node( x, y ) ) - for x in range( 0, width ): - for y in range( 0, height ): - node = nodes[ x ][ y ] - #These are edges to direct neighbours - if x > 0: - node.addEdgeNode( nodes[ x - 1 ][ y ] ) - if y > 0: - node.addEdgeNode( nodes[ x ][ y - 1 ] ) - if x < width - 1: - node.addEdgeNode( nodes[ x + 1 ][ y ] ) - if y < height - 1: - node.addEdgeNode( nodes[ x ][ y + 1 ] ) - #And these are for diagonal neighbours - if x > 0 and y > 0: - node.addEdgeNode( nodes[ x - 1 ][ y - 1 ] ) - if x < width - 1 and y > 0: - node.addEdgeNode( nodes[ x + 1 ][ y - 1 ] ) - if x < width - 1 and y < height - 1: - node.addEdgeNode( nodes[ x + 1 ][ y + 1 ] ) - if x > 0 and y < height - 1: - node.addEdgeNode( nodes[ x - 1 ][ y + 1 ] ) - #This is for setting a node to disabled - if random() >= 0.75: - node.enabled = False - return nodes -## + def __init__(self, posx, posy): + self.position = (posx, posy) + self.cost_g = 0 + self.cost_h = 0 + self.cost_f = 0 + self.opened = False + self.closed = False + self.enabled = True + self.parent = None + self.edges = [] + + def addEdgeNode(self, node): + self.edges.append(node) + + def getEdges(self): + return self.edges + + def getGCostTo(self, node): + cost = 0 + if node is not None: + cost = cost + node.cost_g + if (self.position[0] is node.position[0] + or self.position[1] is node.position[1]): + cost += 10 + else: + cost += 14 + return cost + + def getHCostTo(self, node): + cost = (abs(self.position[0] - node.position[0]) + + abs(self.position[1] - node.position[1])) * 10 + return cost + + def applyCostsToSelf(self, end_node): + self.cost_g = self.getGCostTo(self.parent) + self.cost_h = self.getHCostTo(end_node) + + def getFCost(self): + return self.cost_g + self.cost_h + + +def task1_AStarSearch(start_node, end_node): + if start_node is None: + print("Missing a starting node.") + return None + elif end_node is None: + print("Missing an end node.") + return None + elif start_node.position is end_node.position: + print("Already there.") + return [] + elif not start_node.enabled: + print("Start node blocked.") + return None + elif not end_node.enabled: + print("End node blocked.") + return None + else: + current = start_node + opened = [start_node] + closed = [] + start_node.applyCostsToSelf(end_node) + start_node.opened = True + number_of_iterations = 0 + while (number_of_iterations < 200 + and current is not None): + number_of_iterations += 1 + open_cost = 99999999999 + current = None + for node in opened: + if node.getFCost() < open_cost: + current = node + open_cost = node.getFCost() + if current is end_node: + break + elif current not in opened: + print("Failed, no path available.") + return None + else: + opened.remove(current) + closed.append(current) + current.closed = True + current.opened = False + for node in current.edges: + if not node.enabled or node.closed: + continue + elif node.opened: + if node.cost_g <= node.getGCostTo(current): + continue + else: + opened.append(node) + node.opened = True + node.parent = current + node.applyCostsToSelf(end_node) + for node in opened: + node.opened = False + for node in closed: + node.closed = False + path = [] + while current is not None: + path.append(current) + current = current.parent + result = list(reversed(path)) + print("The path consists of " + str(len(result)) + " nodes.") + return result + + +def task1_buildRandomNodeField(width, height): + nodes = [] + for x in range(0, width): + l = [] + nodes.append(l) + for y in range(0, height): + l.append(Node(x, y)) + for x in range(0, width): + for y in range(0, height): + node = nodes[x][y] + # These are edges to direct neighbours + if x > 0: + node.addEdgeNode(nodes[x - 1][y]) + if y > 0: + node.addEdgeNode(nodes[x][y - 1]) + if x < width - 1: + node.addEdgeNode(nodes[x + 1][y]) + if y < height - 1: + node.addEdgeNode(nodes[x][y + 1]) + # And these are for diagonal neighbours + if x > 0 and y > 0: + node.addEdgeNode(nodes[x - 1][y - 1]) + if x < width - 1 and y > 0: + node.addEdgeNode(nodes[x + 1][y - 1]) + if x < width - 1 and y < height - 1: + node.addEdgeNode(nodes[x + 1][y + 1]) + if x > 0 and y < height - 1: + node.addEdgeNode(nodes[x - 1][y + 1]) + # This is for setting a node to disabled + if random() >= 0.75: + node.enabled = False + return nodes + + +## ## Putting it all together ## -def task2_renderNodes( nodes, dimx, dimy ): - if nodes is None: - return - else: - step_x = 480.0 / dimx - step_y = 480.0 / dimy - init_x = step_x / 2.0 - init_y = step_y / 2.0 - for xs in nodes: - for node in xs: - if node.enabled: - glColor3f( 0.0, 1.0, 0.0 ) - else: - glColor3f( 0.0, 0.0, 1.0 ) - glBegin( GL_POINTS ) - glVertex2f( init_x + step_x * node.position[ 0 ], - init_y + step_y * node.position[ 1 ] ) - glEnd() - -def task2_renderNodePath( path_nodes, dimx, dimy ): - if ( path_nodes is None - or len( path_nodes ) <= 1 ): - return - else: - step_x = 480.0 / dimx - step_y = 480.0 / dimy - init_x = step_x / 2.0 - init_y = step_y / 2.0 - from_pos = path_nodes[ 0 ].position - for node in path_nodes: - to_pos = node.position - glBegin( GL_LINE_STRIP ) - glVertex2f( init_x + step_x * from_pos[ 0 ], - init_y + step_y * from_pos[ 1 ] ) - glVertex2f( init_x + step_x * to_pos[ 0 ], - init_y + step_y * to_pos[ 1 ] ) - glEnd() - from_pos = to_pos +def task2_renderNodes(nodes, dimx, dimy): + if nodes is None: + return + else: + step_x = 480.0 / dimx + step_y = 480.0 / dimy + init_x = step_x / 2.0 + init_y = step_y / 2.0 + for xs in nodes: + for node in xs: + if node.enabled: + glColor3f(0.0, 1.0, 0.0) + else: + glColor3f(0.0, 0.0, 1.0) + glBegin(GL_POINTS) + glVertex2f(init_x + step_x * node.position[0], + init_y + step_y * node.position[1]) + glEnd() + + +def task2_renderNodePath(path_nodes, dimx, dimy): + if (path_nodes is None + or len(path_nodes) <= 1): + return + else: + step_x = 480.0 / dimx + step_y = 480.0 / dimy + init_x = step_x / 2.0 + init_y = step_y / 2.0 + from_pos = path_nodes[0].position + for node in path_nodes: + to_pos = node.position + glBegin(GL_LINE_STRIP) + glVertex2f(init_x + step_x * from_pos[0], + init_y + step_y * from_pos[1]) + glVertex2f(init_x + step_x * to_pos[0], + init_y + step_y * to_pos[1]) + glEnd() + from_pos = to_pos + def task2_rebuildNodes(): - global NODES - global NODE_PATH - NODES = task1_buildRandomNodeField( 10, 10 ) - NODE_PATH = task1_AStarSearch( NODES[ 0 ][ 0 ], NODES[ 9 ][ 9 ] ) - task0_render() + global NODES + global NODE_PATH + NODES = task1_buildRandomNodeField(10, 10) + NODE_PATH = task1_AStarSearch(NODES[0][0], NODES[9][9]) + task0_render() -## + +## ## Start the program ## -task0_initOpenGL() \ No newline at end of file +task0_initOpenGL() From 5f09e5ce173302563d75c2dca8473dc7e98f8952 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 15 Jun 2019 14:47:31 +0200 Subject: [PATCH 44/47] Updated excercise to work with the new api as well as Python 3. --- exercises/spotify/auth_data.py | 3 ++ exercises/spotify/searcher.py | 74 +++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 28 deletions(-) create mode 100644 exercises/spotify/auth_data.py diff --git a/exercises/spotify/auth_data.py b/exercises/spotify/auth_data.py new file mode 100644 index 0000000..9a467e6 --- /dev/null +++ b/exercises/spotify/auth_data.py @@ -0,0 +1,3 @@ +# Login to https://developer.spotify.com/dashboard/, create an application and fill these out before use! +client_id = "" +client_secret = "" \ No newline at end of file diff --git a/exercises/spotify/searcher.py b/exercises/spotify/searcher.py index 5121b1b..d09ad75 100644 --- a/exercises/spotify/searcher.py +++ b/exercises/spotify/searcher.py @@ -1,61 +1,79 @@ import spotipy +import spotipy.oauth2 as oauth2 import sys +import auth_data + +try: + credentials = oauth2.SpotifyClientCredentials( + client_id=auth_data.client_id, + client_secret=auth_data.client_secret) +except oauth2.SpotifyOauthError: + print( + "Invalid client_id or secrept provided, make sure to register the application at " + "https://developer.spotify.com/dashboard/ and then fill out the 'client_id' and 'client_secret' fields in " + "auth_data.py in order to use this application.") + sys.exit(1) + +token = credentials.get_access_token() # Create new API object wrapper -sp = spotipy.Spotify() +spotify = spotipy.Spotify(auth=token) # Format a track: "Track name – Artist, Album" def format_track(track): - name = track['name'].encode('utf8') - album = track['album']['name'].encode('utf8') - artists = ", ".join(map(lambda a: a['name'], track['artists'])).encode('utf8') + name = track['name'] + album = track['album']['name'] + artists = ", ".join(map(lambda a: a['name'], track['artists'])) + + return "{0} - {1} ({2})".format(name, artists, album) - return "{0} - {1} ({2})".format(name, artists, album) # Format an artist: "Artist name" def format_artist(artist): - return artist['name'].encode('utf8') + return artist['name'] + # Print API results with a formatter def show_results(results, formatter): - for i, t in enumerate(results['items'], 1): - print "{0}. {1}".format(i, formatter(t)) + for i, t in enumerate(results['items'], 1): + print("{0}. {1}".format(i, formatter(t))) + # Search Spotify for a query with a limit def search(query, limit): + tracks = spotify.search(q=query, limit=limit) + artists = spotify.search(q=query, limit=limit, type='artist') - tracks = sp.search(q=query, limit=limit) - artists = sp.search(q=query, limit=limit, type='artist') + # Tracks - # Tracks + print("\n") + print("Found {1} tracks with: '{0}'\n".format(query, tracks['tracks']['total'])) - print "\n" - print "Found {1} tracks with: '{0}'\n".format(query, tracks['tracks']['total']) + if tracks['tracks']['total'] > limit: + print("(showing {0} first)".format(limit)) - if tracks['tracks']['total'] > limit: - print "(showing {0} first)".format(limit) + show_results(tracks['tracks'], format_track) - show_results(tracks['tracks'], format_track) + # Artists - # Artists + print("\n") + print("Found {1} artists with: '{0}'\n".format(query, artists['artists']['total'])) - print "\n" - print "Found {1} artists with: '{0}'\n".format(query, artists['artists']['total']) + if artists['artists']['total'] > limit: + print("(showing {0} first)".format(limit)) - if artists['artists']['total'] > limit: - print "(showing {0} first)".format(limit) + show_results(artists['artists'], format_artist) - show_results(artists['artists'], format_artist) + print("\n") - print "\n" # Usage: # # python searcher.py if __name__ == "__main__": - if len(sys.argv) <= 1: - print "You must provide something to search for!" - print "python searcher.py ''" - sys.exit(0) + if len(sys.argv) <= 1: + print("You must provide something to search for!") + print("python searcher.py ''") + sys.exit(0) - search(sys.argv[1], limit=5) + search(sys.argv[1], limit=5) From 72307f29f82456371063a7ef8ea63d8acea5930a Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Sat, 15 Jun 2019 14:47:36 +0200 Subject: [PATCH 45/47] Updated readme. --- exercises/spotify/README.md | 77 +++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/exercises/spotify/README.md b/exercises/spotify/README.md index c090151..a0d7d5a 100644 --- a/exercises/spotify/README.md +++ b/exercises/spotify/README.md @@ -4,33 +4,43 @@ Ett verktyg för att söka i Spotifys bibliotek efter låtar och artister. **Svårighetsgrad: 2** -Denna övning kan göras *otroligt* mycket enklare med hjälp av det färdigskrivna verktyget [Spotipy](https://github.com/plamere/spotipy), som verkar som ett Python-lager kring [Spotifys API](https://developer.spotify.com/web-api). +Denna övning kan göras *otroligt* mycket enklare med hjälp av det färdigskrivna verktyget [Spotipy](https://github.com/plamere/spotipy) ([dokumentation](https://spotipy.readthedocs.io/en/latest/)), som verkar som ett Python-lager kring [Spotifys API](https://developer.spotify.com/web-api). -Ladda ner zip-filen med biblioteket: [https://github.com/plamere/spotipy/archive/master.zip](https://github.com/plamere/spotipy/archive/master.zip), packa upp, och kör följande på en kommandorad inifrån den uppackade mappen: +Varning: dokumentationen för Spotipy är inte alltid updaterad och bör därmed tas med en nypa salt. -```bash -python setup.py install -``` -Nu bör du kunna skriva `import spotipy` överst i en Python-fil och använda det enligt [dokumentationen](https://github.com/plamere/spotipy). Exempel: - -```python -import spotipy - -sp = spotipy.Spotify() -tracks = sp.search(q='Stairway To Heaven') -for i, t in enumerate(tracks['tracks']['items'], 1): - name = t['name'] - print "{1}. {0}".format(name, i) -``` ## Delmoment -1. Använd `sp.search()` för att söka efter en låt och visa resultaten. - 1.1. Visa även album och artist. - -2. Låt koden söka efter en textsträng man ger skriptet: `python searcher.py 'Stairway To Heaven'`. - -3. Lägg till funktion för att söka på artister. +1. **Setup:** För att komma igång och använda spotipy behöver de förfrågningar som skickas till spotify vara autentiserade. För vår applikation där vi endast vill komma åt puplik data som låtar, artister etc är denna process ganska kort däremot. + * Börja med att logga in på https://developer.spotify.com/dashboard/ + * Välj sedan "CREATE AN APP" och fyll i uppgifterna som frågas efter (det fungerar att välja "I don't know" på frågan över vad man bygger för något) . + * Godkänn användaravtal etc. + * När ni har kommit till överblickssidan för er applikation, spara Client ID samt Client Secret (kan behöva tryckas på en "SHOW CLIENT SECRET" knapp för att se denna) på ett lämpligt ställe, antingen i variabler i koden eller i en separat fil. **Viktigt:** Dessa strängar (särskillt Clien_secret) bör hanteras som lösenord och därmed inte t.ex. finnas med i git-hanterade filer eller på annat sätt läggas ut på nätet eller liknande. + * Använd nu client_id samt client_secret för att få credentials och sedan en authentication token till er applikation, exempel: + ```Python + import spotipy.oauth2 as oauth2 + client_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + client_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + credentials = oauth2.SpotifyClientCredentials( + client_id=client_id, + client_secret=client_secret) + + + token = credentials.get_access_token() + ``` + * Med denna token kan ni nu skapa ett spotipy wrapper object som så: + ```python + # Create new API object wrapper + spotify = spotipy.Spotify(auth=token) + ``` + +2. Använd `sotify.search()` ([API dokuementation](https://spotipy.readthedocs.io/en/latest/?highlight=search#spotipy.client.Spotify.search)) för att söka efter en låt och visa resultaten. + 2.1. Visa även album och artist. + +3. Låt koden söka efter en textsträng man ger skriptet: `python searcher.py 'Stairway To Heaven'`. + +4. Lägg till funktion för att söka på artister. Presentera den infon du känner är relevant! Se i [dokumentationen](https://developer.spotify.com/web-api/search-item/) vilka data som finns tillgängliga. @@ -38,26 +48,17 @@ Presentera den infon du känner är relevant! Se i [dokumentationen](https://dev 1. Spela upp de korta förhandsvisningarna av låtarna som finns (se [API-dokumentationen](https://developer.spotify.com/web-api/object-model), `preview_url` för Track Object). Externa bibliotek för ljuduppspelning lär behövas. +2. Lägg till full användar-autentisering och därmed även möjligheten att få tillgång till användarinformation och spela hela låter etc. För detta kan det vara bra att läsa mer av [spotifys autentisering guide](https://developer.spotify.com/documentation/general/guides/authorization-guide/) samt [spotipys autentiserings guide](https://spotipy.readthedocs.io/en/latest/?highlight=authentication#authorized-requests). + ## Externa bibliotek ### Spotipy [Spotipy](https://github.com/plamere/spotipy). Python-wrapper för [Spotifys Web API](https://developer.spotify.com/web-api/). -Spotipy dependar på [Requests](https://github.com/kennethreitz/requests). - -#### Installation - -Ladda ner eller klona mappen från GitHub, ställ dig i mappen och kör: -```bash -python setup.py install -``` +## Installation -Eller gå helt via pakethanterare: -```bash -pip install SpotipyWebAPI -``` -eller -```bash -easy_install SpotipyWebAPI -``` +Det finns flera sätt att installera spotipy, de enklaste är att antingen använda IDEns (exempelvis pycharm) installationsverktyg (om detta finns) eller att använda pip: +```bash +pip install spotipy +``` \ No newline at end of file From be5e1da5ec41b6ad8259850c0c53059cc44c4c56 Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Mon, 24 Jun 2019 19:56:11 +0200 Subject: [PATCH 46/47] Fixed small error --- exercises/chat/online_examples/pairclient.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/chat/online_examples/pairclient.py b/exercises/chat/online_examples/pairclient.py index 4ea9b61..f77cf47 100644 --- a/exercises/chat/online_examples/pairclient.py +++ b/exercises/chat/online_examples/pairclient.py @@ -12,6 +12,6 @@ while True: msg = socket.recv() print(msg) - socket.send("client message to server1") - socket.send("client message to server2") + socket.send_string("client message to server1") + socket.send_string("client message to server2") time.sleep(1) From 1c073e87db4789bf36ac99b7f579a680b6ef1fcb Mon Sep 17 00:00:00 2001 From: Vidar Magnusson Date: Tue, 25 Jun 2019 18:09:29 +0200 Subject: [PATCH 47/47] Formatted file. --- exercises/personnr/personnr.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/exercises/personnr/personnr.py b/exercises/personnr/personnr.py index 5ab7d6d..0520123 100644 --- a/exercises/personnr/personnr.py +++ b/exercises/personnr/personnr.py @@ -1,6 +1,6 @@ - -import sys import datetime +import sys + ######################## ## Functions @@ -12,6 +12,7 @@ def clean(string): """ return string.replace(" ", "").replace("-", "") + def date_is_correct(string): """Return True or False @@ -23,6 +24,7 @@ def date_is_correct(string): return False return True + def string_to_int_list(string): """Return list of integers @@ -30,8 +32,8 @@ def string_to_int_list(string): '1234' -> [1,2,3,4] """ return map(int, list(string)) - - + + def number_is_correct(pnr): """Returns True or False @@ -39,17 +41,18 @@ def number_is_correct(pnr): """ factor = 2 c = 0 - for x in pnr : - c = c + x*factor - if(x*factor > 9): - c = c -9 - + for x in pnr: + c = c + x * factor + if (x * factor > 9): + c = c - 9 + if factor == 2: factor = 1 - else : + else: factor = 2 return c % 10 == 0 + def check(str): """Returns (True/False, Explanation) @@ -59,7 +62,7 @@ def check(str): if len(cleaned) != 10: return (False, 'The personal number must include ten digits') - + if not date_is_correct(cleaned): return (False, 'Error in date') @@ -68,6 +71,7 @@ def check(str): return (True, 'Congratulations, the number is correct') + ######################## ## Main