· 6 years ago · Dec 16, 2019, 07:10 PM
1#_ __ __ _ _ ____
2# | \/ | __ _| |_ ___| |__ / ___| __ _ _ __ ___ ___
3# | |\/| |/ _` | __/ __| '_ \ | | _ / _` | '_ ` _ \ / _ \
4# | | | | (_| | || (__| | | | | |_| | (_| | | | | | | __/
5# |_| |_|\__,_|\__\___|_| |_| \____|\__,_|_| |_| |_|\___|
6#
7botName='d7600013-defbot'
8import requests
9import json
10from random import sample, choice
11from time import sleep
12
13# See our help page to learn how to get a WEST EUROPE Microsoft API Key at
14# https://help.aigaming.com/game-help/signing-up-for-azure
15# *** Use westeurope API key for best performance ***
16headers_vision = {'Ocp-Apim-Subscription-Key': '23115f42f1a54d28820a78c3b0660ab1'}
17vision_base_url = "https://westeurope.api.cognitive.microsoft.com/vision/v2.0/"
18
19analysed_tiles = []
20previous_move = []
21move_number = 0
22
23gamebacks = {}
24gamebacks["https://aigaming.blob.core.windows.net/match-game/3685260b-721c-4b1c-b3c5-c030cd4e8080/813c5578-3fbd-4d42-a285-754c5b567a90.jpg"] = "Word"
25gamebacks["https://aigaming.blob.core.windows.net/match-game/3685260b-721c-4b1c-b3c5-c030cd4e8080/02b22f57-1e01-4c1b-9784-a94ab5468219.jpg"] = "Animal"
26gamebacks["https://aigaming.blob.core.windows.net/match-game/3685260b-721c-4b1c-b3c5-c030cd4e8080/ee3272f6-e12c-4ad1-a1ad-42b50e99ed8a.jpg"] = "Landmark"
27
28
29great_Confidence = {}
30
31# =============================================================================
32# calculate_move() overview
33# 1. Analyse the upturned tiles and remember them
34# 2. Determine if you have any matching tiles
35# 3. If we have matching tiles:
36# use them as a move
37# 4. If no matching tiles:
38# Guess two tiles for this move
39#
40# **Important**: calculate_move() can only remember data between moves
41# if we store data in a global variable
42# Use the analysed_tiles global to remember the tiles we have seen
43# We recognise animals for you, you must add Landmarks and Words
44#
45# Get more help on the Match Game page at https://help.aigaming.com
46#
47def calculate_move(gamestate):
48 global analysed_tiles
49 global previous_move
50 global move_number
51
52 # Record the number of tiles so we know how many tiles we need to loop through
53 num_tiles = len(gamestate["Board"])
54 move_number += 1
55 if gamestate["UpturnedTiles"] == []:
56 print("{}. No upturned tiles for this move.".format(move_number))
57 else:
58 print("{}. ({}, {}) Upturned tiles for this move".format(move_number, gamestate["UpturnedTiles"][0]["Index"], gamestate["UpturnedTiles"][1]["Index"]))
59 print(" gamestate: {}".format(gamestate))
60
61 # If we have not yet used analysed_tiles (i.e. It is the first turn of the game)
62 if analysed_tiles == []:
63 # Create a list to hold tile information and set each one as UNANALYSED
64 for index in range(num_tiles):
65 # Mark tile as not analysed
66 analysed_tiles.append({})
67 analysed_tiles[index]["State"] = "UNANALYSED"
68 analysed_tiles[index]["Subject"] = None
69
70 # The very first move in the game does not have any upturned tiles, and
71 # if your last move matched tiles, you will not have any upturned tiles
72
73 # Check to see if we have received some upturned tiles for this move.
74 if gamestate["UpturnedTiles"] != []:
75 # Analyse the tile images using the Microsoft API and store the results
76 # in analysed_tiles so that we don't have to analyse them againf if we
77 # see the same tile later in the game.
78 analyse_tiles(gamestate["UpturnedTiles"], gamestate)
79 # Else, it is either our first turn, or, our previous move was a match
80 else:
81 # If it is not our first move of the game
82 if previous_move != []:
83 # then our previous move successfully matched two tiles
84 # Update our analysed_tiles to mark the previous tiles as matched
85 print(" MATCH: ({}, {}) - {}".format(previous_move[0], previous_move[1], analysed_tiles[previous_move[0]]["Subject"]))
86 analysed_tiles[previous_move[0]]["State"] = "MATCHED"
87 analysed_tiles[previous_move[1]]["State"] = "MATCHED"
88
89 # TIP: Python print statements appear in column 3 of this Editor window
90 # and can be used for debugging
91 # Print out the updated analysed_tiles list to see what it contains
92 #print("Analysed Tiles: {}".format(json.dumps(analysed_tiles, indent=2)))
93
94 # Check the stored tile information in analysed_tiles
95 # to see if we know of any matching tiles
96 match = search_for_matching_tiles()
97 # If we do have some matching tiles
98 if match is not None:
99 # Print out the move for debugging ----------------->
100 print(" Matching Move: {}".format(match))
101 # Set our move to be these matching tiles
102 move = match
103 # If we don't have any matching tiles
104 else:
105 # Create a list of all the tiles that we haven't analysed yet
106 unanalysed_tiles = get_unanalysed_tiles()
107 # If there are some tiles that we haven't analysed yet
108 if unanalysed_tiles != []:
109 # Choose the unanalysed tiles that you want to turn over
110 # in your next move. We turn over a random pair of
111 # unanalysed tiles, but, could you make a more intelligent
112 # choice?
113 move = sample(unanalysed_tiles, 2)
114 # Print out the move for debugging ----------------->
115 print(" New tiles move: {}".format(move))
116 # If the unanalysed_tiles list is empty (all tiles have been analysed)
117 else:
118 # If all else fails, we will need to manually match each tile
119
120 # Create a list of all the unmatched tiles
121 unmatched_tiles = get_unmatched_tiles()
122
123 # Turn over two random tiles that haven't been matched
124 # TODO: It would be more efficient to remember which tiles you
125 # have tried to match.
126
127 bonus_category = gamestate["Bonus"]
128 tiles_to_choose = []
129 for index in range(len(gamestate["Board"])):
130 if gamestate["Board"][index] == 0 and gamebacks["TileBacks"][index] == bonus_category:
131 tiles_to_choose.append(index)
132
133
134
135 #nehme unmatched_tiles ist aus gamestate category
136 #if teste unmatch in match:
137 # nehme match
138 #else:
139 # nehme anderes unmatched
140 move = []
141 if(len(tiles_to_choose) >0):
142 move = sample(tiles_to_choose, 2)
143 else:
144 move = sample(unmatched_tiles, 2)
145
146 # Print the move for debugging ----------------->
147 print(" Random guess move: {}".format(move))
148
149 # Store our move to look back at next turn
150 previous_move = move
151 # Return the move we wish to make
152 return {"Tiles": move}
153
154
155# Get the unmatched tiles
156#
157# Outputs:
158# list of integers - A list of unmatched tile numbers
159#
160# Returns the list of tiles that haven't been matched
161def get_unmatched_tiles():
162 # Create a list of all the unmatched tiles
163 unmatched_tiles = []
164 # For every tile in the game
165 for index, tile in enumerate(analysed_tiles):
166 # If that tile hasn't been matched yet
167 if tile["State"] != "MATCHED":
168 # Add that tile to the list of unmatched tiles
169 unmatched_tiles.append(index)
170 # Return the list
171 return unmatched_tiles
172
173
174
175
176
177# Identify all of the tiles that we have not yet analysed with the
178# Microsoft API.
179#
180# Output:
181# list of integers - only those tiles that have not yet been analysed
182# by the Microsoft API
183#
184# Returns the list of tiles that haven't been analysed
185# (according to analysed_tiles)
186def get_unanalysed_tiles():
187 # Filter out analysed tiles
188 unanalysed_tiles = []
189 # For every tile that hasn't been matched
190 for index, tile in enumerate(analysed_tiles):
191 # If the tile hasn't been analysed
192 if tile["State"] == "UNANALYSED":
193 # Add that tile to the list of unanalysed tiles
194 unanalysed_tiles.append(index)
195 # Return the list
196 return unanalysed_tiles
197
198
199# Analyses a list of tiles
200#
201# Inputs:
202# tiles: list of JSON objects - A list of tile objects that contain a
203# url and an index
204# gamestate: JSON object - The current state of the game
205#
206# Given a list of tiles we want to analyse and the animal list, calls the
207# analyse_tile function for each of the tiles in the list
208def analyse_tiles(tiles, gamestate):
209 # For every tile in the list 'tiles'
210 for tile in tiles:
211 # Call the analyse_tile function with that tile
212 # along with the gamestate
213 analyse_tile(tile, gamestate)
214
215
216# Analyses a single tile
217#
218# Inputs:
219# tile: JSON object - A tile object that contains a url and an index
220# gamestate: JSON object - The current state of the game
221#
222# Given a tile, analyse it to determine its subject and record the information
223# in analysed_tiles using the Microsoft APIs
224def analyse_tile(tile, gamestate):
225 # If we have already analysed the tile
226 if analysed_tiles[tile["Index"]]["State"] != "UNANALYSED":
227 # We don't need to analyse the tile again, so stop
228 return
229
230 # Call analysis
231 analyse_url = vision_base_url + "analyze"
232 params_analyse = {'visualFeatures': 'categories,tags,description,faces,imageType,color,adult',
233 'details': 'celebrities,landmarks'}
234 data = {"url": tile["Tile"]}
235 msapi_response = microsoft_api_call(analyse_url, params_analyse, headers_vision, data)
236 print(" API Result tile #{}: {}".format(tile["Index"], msapi_response))
237 # Check if the subject of the tile is a landmark
238 subject = check_for_landmark(msapi_response)
239 # If we haven't determined the subject of the image yet
240 if subject is None:
241 # Check if the subject of the tile is an animal
242 subject = check_for_animal(msapi_response, gamestate["AnimalList"])
243 # If we still haven't determined the subject of the image yet
244 if subject is None:
245 # TODO: Use the Microsoft OCR API to determine if the tile contains a
246 # word. You can get more information about the Microsoft Cognitive API
247 # OCR function at:
248 # https://westus.dev.cognitive.microsoft.com/docs/services/56f91f2d778daf23d8ec6739/operations/56f91f2e778daf14a499e1fc
249 # Use our previous example to check_for_animal as a guide
250 subject = check_for_text(tile)
251
252 pass
253 else:
254 print(" Animal at tile #{}: {}".format(tile["Index"], subject))
255 # Remember this tile by adding it to our list of known tiles
256 # Mark that the tile has now been analysed
257 analysed_tiles[tile["Index"]]["State"] = "ANALYSED"
258 analysed_tiles[tile["Index"]]["Subject"] = subject
259
260
261# Check Microsoft API response to see if it contains information about an animal
262#
263# Inputs:
264# msapi_response: JSON dictionary - A dictionary containing all the
265# information the Microsoft API has returned
266# animal_list: list of strings - A list of all the possible animals in
267# the game
268# Outputs:
269# string - The name of the animal
270#
271# Given the result of the Analyse Image API call and the list of animals,
272# returns whether there is an animal in the image
273def check_for_animal(msapi_response, animal_list):
274 # Initialise our subject to None
275 subject = None
276 # If the Microsoft API has returned a list of tags
277 if "tags" in msapi_response:
278 # Loop through every tag in the returned tags, in descending confidence order
279 for tag in sorted(msapi_response["tags"], key=lambda x: x['confidence'], reverse=True):
280 # If the tag has a name and that name is one of the animals in our list
281 if "name" in tag and tag["name"] in animal_list:
282 # Record the name of the animal that is the subject of the tile
283 # (We store the subject in lowercase to make comparisons easier)
284 subject = tag["name"].lower()
285 # Print out the animal we have found here for debugging ----------------->
286 # print(" Animal: {}".format(subject))
287 # Exit the for loop
288 break
289 # Return the subject
290 return subject
291
292
293def check_for_text(tile):
294
295 subject = None
296
297 analyse_url = vision_base_url + "ocr"
298 params_analyse = {}
299 data = {"url": tile["Tile"]}
300 msapi_response = microsoft_api_call(analyse_url, params_analyse, headers_vision, data)
301 print("OCR Response: {}".format(msapi_response))
302 if "regions" in msapi_response:
303 if "lines" in msapi_response["regions"][0]:
304 if "words" in msapi_response["regions"][0]["lines"][0]:
305 if "text" in msapi_response["regions"][0]["lines"][0]["words"][0]["text"]:
306 subject = msapi_response["regions"][0]["lines"][0]["words"][0]["text"]
307 print(subject)
308 return subject
309
310
311
312# ----------------------------------- TODO -----------------------------------
313#
314# Inputs:
315# msapi_response: JSON dictionary - A dictionary containing all the
316# information the Microsoft API has returned
317# Outputs:
318# string - The name of the landmark
319#
320# Given the result of the Analyse Image API call, returns whether there is a
321# landmark in the image
322#
323# NOTE: you don't need a landmark_list like you needed an animal_list in check_for_animal
324#
325def check_for_landmark(msapi_response): # NOTE: you don't need a landmark_list
326 # TODO: We strongly recommend copying the result of the Microsoft API into
327 # a JSON formatter (e.g. https://jsonlint.com/) to better understand what
328 # the API is returning and how you will access the landmark information
329 # that you need.
330 # Here is an example of accessing the information in the JSON:
331 # msapi_response["categories"][0]["detail"]["landmarks"][0]["name"].lower()
332
333 # Initialise our subject to None
334 subject = None
335 # For every category in the returned categories
336 for category in msapi_response["categories"]:
337 # If the category has detail provided, and the detail contains a landmark
338 if "detail" in category \
339 and "landmarks" in category["detail"] \
340 and category["detail"]["landmarks"]:
341 # Record the name of the landmark that is the subject of the tile
342 # (We usually store the subject in lowercase to make comparisons easier)
343 subject = category["detail"]["landmarks"][0]["name"].lower()
344 # Print the landmark we have found here ----------------->
345 print("Landmark: {}".format(subject))
346 # Exit the for loop
347 break
348 # Return the subject
349 return subject
350
351
352# Find matching tile subjects
353#
354# Outputs:
355# list of integers - A list of two tile indexes that have matching subjects
356#
357# Search through analysed_tiles for two tiles recorded
358# under the same subject
359def search_for_matching_tiles():
360 # For every tile subject and its index
361 for index_1, tile_1 in enumerate(analysed_tiles):
362 # Loop through every tile subject and index
363 for index_2, tile_2 in enumerate(analysed_tiles):
364 # If the two tile's subject is the same and isn't None and the tile
365 # hasn't been matched before, and the tiles aren't the same tile
366 if tile_1["State"] == tile_2["State"] == "ANALYSED" and tile_1["Subject"] == tile_2["Subject"] and tile_1["Subject"] is not None and index_1 != index_2:
367 # Choose these two tiles
368 # Return the two chosen tiles as a list
369 return [index_1, index_2]
370 # If we have not matched any tiles, return no matched tiles
371 return None
372
373
374# Call the Microsoft API
375#
376# Inputs:
377# url: string - The url to the Microsoft API
378# params: dictionary - Configuration parameters for the request
379# headers: dictionary - Subscription key to allow request to be made
380# data: dictionary - Input to the API request
381# Outputs:
382# JSON dictionary - The result of the API call
383#
384# Given the parameters, makes a request to the specified url, the request is
385# retried as long as there is a volume quota error
386def microsoft_api_call(url, params, headers, data):
387 # Make API request
388 response = requests.post(url, params=params, headers=headers, json=data)
389 # Convert result to JSON
390 res = response.json()
391 # While we have exceeded our request volume quota
392 while "error" in res and res["error"]["code"] == "429":
393 # Wait for 1 second
394 sleep(1)
395 # Print that we are retrying the API call here ----------------->
396 print("Retrying")
397 # Make API request
398 response = requests.post(url, params=params, headers=headers, json=data)
399 # Convert result to JSON
400 res = response.json()
401 # Print the result of the API call here for debugging ----------------->
402 # print(" API Result: {}".format(res))
403 # Return JSON result of API request
404 return res
405
406
407# Test the user's subscription key
408#
409# Raise an error if the user's API key is not valid for the Microsoft
410# Computer Vision API call
411def valid_subscription_key():
412 # Make a computer vision api call
413 test_api_call = microsoft_api_call(vision_base_url + "analyze", {}, headers_vision, {})
414
415 if "error" in test_api_call:
416 raise ValueError("Invalid Microsoft Computer Vision API key for current region: {}".format(test_api_call))
417
418
419# Check the subscription key
420valid_subscription_key()