Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 Artificial Intelligence in Games Week 5 Steve Rabin TA: Chi-Hao Kuo TA: Allan.

Similar presentations


Presentation on theme: "1 Artificial Intelligence in Games Week 5 Steve Rabin TA: Chi-Hao Kuo TA: Allan."— Presentation transcript:

1 1 Artificial Intelligence in Games Week 5 Steve Rabin steve.rabin@gmail.com www.aiwisdom.com/digipen TA: Chi-Hao Kuo (chihao.kuo@digipen.edu) TA: Allan Deutsch (allan.d@digipen.edu)

2 2 Project 1B: Behavior Trees How did it go? Thoughts? If you didn’t turn it in… You must email it! One day late 50% penalty (up to 33 hours late – 3AM)

3 3 Questions 1. Group emergent behavior: The group behavior emerges from what? 2. Flocking: What are the individual agents called? 3. Flocking: Name the 3 primary steering behaviors. 4. Flocking: Name the other 3 helpful steering behaviors. 5. Flocking: How is the leader chosen? 6. Name as many steering behaviors as you can. 7. Formations: Describe the 2 levels of steering.

4 4

5 5 Swarm of Nano Quadroters!!! http://www.youtube.com/watch?v=YQIMGV5vtd4

6 6 News Flash!!! Dateline: Thu Oct 1, 6:06 AM http://ca.news.yahoo.com/s/afp/091001/oddi ties/japan_auto_technology_offbeat

7 7 "Cars may one day mimic fish to avoid collisions" TOKYO (AFP) - Engineers in Japan say they are a step closer to developing technology they hope will cut the risk of car crashes -- by mimicking the behavior of fish. The experts at Nissan Motor have been studying fish and the way they are able to swim in schools and avoid colliding with each other.

8 8 Nissan: Welcome back to 1987!!!

9 9 Cars may one day mimic fish to avoid collisions The result is a robot that can travel in a group of up to seven, avoiding bumps by sharing information with its peers. The firm hopes to use the technology in its vehicles in future. The three-wheeled robot uses a laser range finder, which measures the distance to an obstacle, and radio communications to recreate the behavior of fish, which can change direction and travel side by side without colliding.

10 10 Cars may one day mimic fish to avoid collisions Last year Nissan unveiled a similar robot inspired by the bumblebee, which is also highly adept at avoiding collisions but travels solo. "We, in a motorized world, have a lot to learn from the behavior of a school of fish in terms of each fish's degree of freedom and safety," said Toshiyuki Andou, the principal engineer in the project. By sharing information, the group can travel safely, changing its shape as needed, Andou said. Nissan will demonstrate the technology at the CEATEC electronics trade fair in Japan next week.

11 11 Movement Two types Reactive movement Planned movement

12 12 Planned Movement Define a search space representation Apply a search algorithm to find a path Optional: Post-process path Agent follows path

13 13 Search Space Representations Represent terrain with a data structure What kind? Search data structure to find a path from point A to point B Let's brainstorm some types of graph data structures...

14 14 Search Space Representations: Important Questions to Ask How does search space scale? Larger search space = slower search How does search space get made? Level Designer or Algorithm? Does search space represent safe paths or safe walkable areas or both?

15 15 Search Space Representations Regular grids Waypoint graphs Corner graphs Circle-based waypoint graphs Navigation meshes

16 16 Search Space Representations: Regular Grid

17 17 Search Space Representations: Regular Grid Rectangular Grid vs Hex Grid Hex grids How do you index them? Advantages? Disadvantages?

18 18 Search Space Representations: Waypoint Graphs Must be hand-placed Common in FPS games

19 19 Search Space Representations: Corner Graphs (Points of Visibility)

20 20 Search Space Representations: Circle-based Waypoint Graphs Waypoints that only connect to overlapping circles Characters can move freely within a circle Good for open terrain Bad for angular environments

21 21 Search Space Representations: Navigation Meshes Types Triangle mesh N-sided polygon mesh Ideal for 3D games Can be extracted from level geometry

22 22 Which Search Space Representation? Consider Amount of designer intervention Amount of technology that must be developed Scalability Terrain type Local pathfinding Grid representation Essentially 2D games Successful in RTS genre with 100’s to 1000’s of agents Waypoint representation 3D FPS type environments with few agents Hand placement and local pathfinding limit viability with ever increasing worlds Navigation Mesh 3D games with many complex terrain features (like pits) Can be automated

23 23 Local Obstacles Characters, crates, etc not represented in search space What can be done? What about pits?

24 24 Ledges and Jumps How should this work? How should the path planning algorithm deal with this?

25 25 Doors and Elevators Elevator requires: Hit button Wait for door to open Pass through door

26 26 Intermission

27 Origin of the word “Robot” 1920 Czech play: “Rossum’s Universal Robots” 27

28 28

29 29

30 30

31 31

32 32

33 33

34 34

35 FDA-approved SpineAssist spine-surgery robot Propelled by micro legs 1mm x 14mm 35

36 36

37 37

38 38

39 39

40 Real Flying Robot Bugs Robotic insects make first controlled flight https://www.youtube.com/watch?v=cyjKOJhIiuU 40

41 41 Pathfinding Planning your Path with Intelligent Search

42 42 Pathfinding Goal is to find an optimal path if one exists Optimal? Is that the shortest path? Search algorithms explore the search space to find a path from the start to the goal Many different search algorithms Random Bounce/Trace Breadth-First Best-First Dijkstra A*

43 43 Criteria to Judge Pathfinding Algorithms Quality of path Shortest, cheapest Resource requirements CPU, memory, time Completeness Will find a path if one exists

44 44 Thinking about Algorithms: Sorting What’s the stupidest/worst algorithm you could come up with for sorting?

45 45 Thinking about Algorithms: Sorting Luckysort Already sorted, O(0) Bozosort Randomly swap two elements and test if sorted, repeat Bogosort function bogosort(array) while !is_sorted(array) array = random_permutation(array)

46 46 Pathfinding Algorithms What’s the stupidest/worst algorithm you could come up with for pathfinding?

47 47 Random Bounce Search directly toward the goal If obstacle Randomly move in a direction Repeat until goal reached

48 48 Random Bounce Not a complete search algorithm Path found is likely not optimal Consumes very little memory

49 49 Random Trace Search directly toward the goal If obstacle Randomly trace clockwise or counter- clockwise around it Repeat until goal reached

50 50 Random Trace How will Random trace do on the following maps?

51 51 Random Trace Simple version not a complete search algorithm With enough tweeking, maybe it can be Path found is likely not optimal Consumes very little memory

52 52 Working Toward A*: Terminology Search Space – connected graph to be explored Node – the individual locations of the search space Start Node – the place where the search will begin Goal Node – the goal location of the search Heuristic – a function, h(n), that returns an estimate of the cheapest cost from a Node to the Goal Node Admissible – if h(n) never overestimates the cost of getting to the Goal Node

53 53 Working Toward A*: Open and Closed Lists Concept of Open and Closed Lists Open List: Promising nodes not yet explored Closed List: Nodes already explored

54 54 Working Toward A*: General Structure of Algorithms 1. Push Start Node onto the Open List 2. While (Open List not empty) a. Pop a node from the Open List (parent node) b. If node is Goal Node, then path found c. Find neighboring child nodes and add them to the Open List d. Place parent node on the Closed List

55 55 Working Toward A*: What Does a Node Look Like? Like this? class PlannerNode { public: PlannerNode*m_pParent; intm_cellX, m_cellY;... };

56 56 Breadth-First Explores the search space ply-by-ply

57 57 Breadth-First Characteristics It is a complete algorithm Exhaustive and systematic but not very clever High Memory Consumption Finds the path with fewest nodes Not the shortest or cheapest path!

58 58 Best-First Uses problem specific knowledge Heads straight for the goal Computes estimated distance of node to goal Heuristic, h(x), to order Open List

59 59 Best-First: Problem Case

60 60 Best-First Characteristics It is a complete algorithm Heuristic search Uses fewer resources than Breadth-First Finds good paths Doesn’t guarantee optimal path

61 61 Dijkstra Uses accumulated distance to order nodes on Open List Accumulated cost from Start Node to current node is called given cost, g(x) Note: not a heuristic search No estimation involved

62 62 Dijkstra It is a complete algorithm It is an exhaustive search Always finds the most optimal path About as resource intensive as Breadth- First

63 63 Intermission: Great Moments in Game Art History

64 64 Intermission: Great Moments in Game Art History

65 65 Intermission: Great Moments in Game Art History

66 66 Intermission: Great Moments in Game Art History

67 67 Intermission: Great Moments in Game Art History

68 68 Intermission: Great Moments in Game Art History

69 69 Intermission: Great Moments in Game Art History

70 70

71 71 A* Combines Best-First and Dijkstra Given cost and Heuristic cost Open List sorted by: Final cost = Given cost + (Heuristic cost * weight) f(x) = g(x) + (h(x) * weight) Consider what different weights do like 0, 1, and 2

72 72 A* Doesn’t fall into Best-First trap

73 73 A* Characteristics It is a complete algorithm Heuristic search On average uses fewer resources than Breadth-First and Dijkstra If admissible heuristic used, guarantees optimal path Admissible – if h(n) never overestimates the cost of getting to the Goal Node How do you not overestimate the cost? What happens if you do overestimate it?

74 74 A* Algorithm: Walkthrough Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

75 75 A* Algorithm: Initial: Start/Goal Nodes, Lists empty Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

76 76 A* Algorithm: 3 ways to exit Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

77 77 A* Algorithm: Prime the algorithm with the 1 st node Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

78 78 A* Algorithm: While nodes still on Open List Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

79 79 A* Algorithm: No path found Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

80 80 A* Algorithm: Get next parent node to work with Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

81 81 A* Algorithm: Goal found? Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

82 82 A* Algorithm: Loop through child nodes Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

83 83 A* Algorithm: Compute child cost, f(x) Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

84 84 A* Algorithm: Put child node on Open List? (easy) Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

85 85 A* Algorithm: Put child node on Open List? (careful!) Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

86 86 A* Algorithm: Done with parent node Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

87 87 A* Algorithm: Aborting this frame (for single step) Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

88 88 A* Algorithm: 3 ways to return Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

89 89 A* Algorithm: Walkthrough Push Start Node onto the Open List While (Open List is not empty) { Pop cheapest node off Open List (parent node) If node is the Goal Node, then path found (RETURN “found”) For (all neighboring child nodes) { Compute its cost, f(x) = g(x) + h(x) If child node isn’t on Open or Closed list, put it on Open List. If child node is on Open or Closed List, AND this new one is cheaper, then take the old expensive one off both lists and put this new cheaper one on the Open List. } Place parent node on the Closed List (we’re done with it) If taken too much time this frame (or in single step mode), abort search for now and resume next frame (RETURN “working”) } Open List empty, thus no path possible (RETURN “fail”)

90 90 A* Worksheet

91 91 A* Details Node data (for a given square) parentRow;//Parent node (-1 is start node) parentCol;//Parent node (-1 is start node) float cost;//Cost to get to this node, g(x) float total;//Total cost, g(x) + h(x) bool onOpen;//On Open List bool onClosed;//On Closed List

92 92 Project 2: A* Pathfinding (due week 8) Absolute requirement: Draw the open list of quads as one color, the closed list of quads as another color, draw the final path, and have Tiny follow the path Grading (20%) Draw one step of A* each frame (use toggle) (15%) A path is always found if it exists (completeness) (15%) The path is optimal with a heuristic weight of 1.0 (15%) The path is optimal with a heuristic weight of 0.0, and the search grows in a circular pattern (5%) The path uses diagonals and doesn't run into corners (5%) Calculate heuristic using Euclidean, Octile, Chebyshev, and Manhattan methods (use toggle) (5%) Straightline optimization when possible (use toggle) (10%) Rubberband final path where possible (use toggle) (10%) Smooth using a Catmull-Rom spline (use toggle) (-10%) If it crashes at any time

93 93 Project 2: A* Pathfinding Extra Credit (bells and whistles) 1 bell = 10% added to project grade =

94 94 Project 2: A* Pathfinding Extra Credit Implement Roy-Floyd-Warshall algorithm (in addition to A*). Must offer on-screen button to toggle on/off. Implement Goal Bounding. Must offer an on- screen toggle to switch between A* and Goal Bounding. Hook it up to the timing.

95 95 Project 2: A* Pathfinding Extra Credit Be in top 3 fastest class times on an A* search. Beat 10 percent exe (Rabin) OR Beat 15 percent exe (Fernando) OR Beat 20 percent exe (Trent Reed)

96 96 Project 2: A* Pathfinding Watching the search Escape after each time through search loop Use quads to mark search Open list Closed list Draw final path with line segments Waypoint to waypoint

97 97 Tiny Pathfinding Demo

98 98 Roy-Floyd-Warshall All-Pairs Shortest Path Pre-compute ALL paths Just look up answer in a table! 1959 Bernard Roy 1962 Robert Floyd 1962 Stephen Warshall

99 99 Roy-Floyd-Warshall All-Pairs Shortest Path Table A 4 ABCDE A B C D E B C D E 2 10 3 4 5

100 100 Roy-Floyd-Warshall All-Pairs Shortest Path O(n 3 ) int path[][]; //initialize to starting costs void FloydWarshall() { for (k = 1 to n) { for each (i,j) in (1..n) { path[i][j] = min ( path[i][j], path[i][k] + path[k][j] ); }

101 101 Roy-Floyd-Warshall All-Pairs Shortest Path Table A 4 ABCDE A0 B0 C0 D0 E0 B C D E 2 10 3 4 5

102 102 Roy-Floyd-Warshall All-Pairs Shortest Path Table A 4 ABCDE A24 B3 C410 D3 4 E5 B C D E 2 3 4 5

103 103 Roy-Floyd-Warshall All-Pairs Shortest Path Table A 4 ABCDE A024 B03 C4010 D3 04 E50 B C D E 2 3 4 5

104 104 Roy-Floyd-Warshall All-Pairs Shortest Path Table A 4 ABCDE A024∞∞ B∞0∞3∞ C4∞010∞ D∞3 04 E∞∞5∞0 B C D E 2 3 4 5

105 105 Roy-Floyd-Warshall All-Pairs Shortest Path Table A 4 ABCDE A024∞∞ B∞0∞3∞ B C D E 2 10 3 4 5 for (k = 1 to n) { for each (i,j) in (1..n) { path[i][j] = min (path[i][j], path[i][k] + path[k][j]); }

106 106 Roy-Floyd-Warshall All-Pairs Shortest Path Table A 4 ABCDE A024∞∞ B∞0∞3∞ B C D E 2 10 3 4 5 for (k = 1 to n) { for each (i,j) in (1..n) { //example i=A, j=D, k=B path[i][j] = min (path[i][j], path[i][k] + path[k][j]); }

107 107 Roy-Floyd-Warshall All-Pairs Shortest Path Table A 4 ABCDE A0245∞ B∞0∞3∞ B C D E 2 10 3 4 5 for (k = 1 to n) { for each (i,j) in (1..n) { //example i=A, j=D, k=B path[i][j] = min (path[i][j], path[i][k] + path[k][j]); }

108 108 Roy-Floyd-Warshall All-Pairs Shortest Path Table A 4 ABCDE A02459 B1601237 C460913 D 3904 E9115140 B C D E 2 10 3 4 5

109 109 Roy-Floyd-Warshall (Represents ideal heuristic) A 4 ABCDE A02459 B1601237 C460913 D 3904 E9115140 B C D E 2 10 3 4 5

110 110 Roy-Floyd-Warshall Inspired Next Node Look-up Table A 4 ABCDE AABCBB BDBDDD CAACAA DEBEDE ECCCCE B C D E 2 10 3 4 5

111 111 Roy-Floyd-Warshall Inspired Next Node Look-up Table Speed of search? Cost to compute? What's the downside during runtime? Which search space representations does it work on?


Download ppt "1 Artificial Intelligence in Games Week 5 Steve Rabin TA: Chi-Hao Kuo TA: Allan."

Similar presentations


Ads by Google