Andy Lee commited on
Commit
b79fff8
·
1 Parent(s): 95c127a

revert: geoguessur_bot

Browse files
Files changed (1) hide show
  1. geoguesser_bot.py +211 -0
geoguesser_bot.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from io import BytesIO
2
+ import os
3
+ import dotenv
4
+ import base64
5
+ import pyautogui
6
+ import matplotlib.pyplot as plt
7
+ import math
8
+ from time import time, sleep
9
+ from typing import Tuple, List
10
+ from PIL import Image
11
+
12
+ from langchain_core.messages import HumanMessage, BaseMessage
13
+ from langchain_openai import ChatOpenAI
14
+ from langchain_anthropic import ChatAnthropic
15
+ from langchain_google_genai import ChatGoogleGenerativeAI
16
+
17
+ dotenv.load_dotenv()
18
+
19
+
20
+ PROMPT_INSTRUCTIONS = """
21
+ Try to predict where the image was taken.
22
+ First describe the relevant details in the image to do it.
23
+ List some regions and places where it could be.
24
+ Chose the most likely Country and City or Specific Location.
25
+ At the end, in the last line a part from the previous reasoning, write the Latitude and Longitude from that guessed location
26
+ using the following format, making sure that the coords are valid floats, without anything else and making sure to be consistent with the format:
27
+ Lat: XX.XXXX, Lon: XX.XXXX
28
+ """
29
+
30
+
31
+ class GeoBot:
32
+ prompt_instructions: str = PROMPT_INSTRUCTIONS
33
+
34
+ def __init__(self, screen_regions, player=1, model=ChatOpenAI, model_name="gpt-4o"):
35
+ self.player = player
36
+ self.screen_regions = screen_regions
37
+ self.screen_x, self.screen_y = screen_regions["screen_top_left"]
38
+ self.screen_w = screen_regions["screen_bot_right"][0] - self.screen_x
39
+ self.screen_h = screen_regions["screen_bot_right"][1] - self.screen_y
40
+ self.screen_xywh = (self.screen_x, self.screen_y, self.screen_w, self.screen_h)
41
+
42
+ self.map_x, self.map_y = screen_regions[f"map_top_left_{player}"]
43
+ self.map_w = screen_regions[f"map_bot_right_{player}"][0] - self.map_x
44
+ self.map_h = screen_regions[f"map_bot_right_{player}"][1] - self.map_y
45
+ self.minimap_xywh = (self.map_x, self.map_y, self.map_w, self.map_h)
46
+
47
+ self.next_round_button = (
48
+ screen_regions["next_round_button"] if player == 1 else None
49
+ )
50
+ self.confirm_button = screen_regions[f"confirm_button_{player}"]
51
+
52
+ self.kodiak_x, self.kodiak_y = screen_regions[f"kodiak_{player}"]
53
+ self.hobart_x, self.hobart_y = screen_regions[f"hobart_{player}"]
54
+
55
+ # Refernece points to calibrate the minimap everytime
56
+ self.kodiak_lat, self.kodiak_lon = (57.7916, -152.4083)
57
+ self.hobart_lat, self.hobart_lon = (-42.8833, 147.3355)
58
+
59
+ self.model = model(model=model_name)
60
+
61
+ @staticmethod
62
+ def pil_to_base64(image: Image) -> str:
63
+ buffered = BytesIO()
64
+ image.save(buffered, format="PNG")
65
+ img_base64_str = base64.b64encode(buffered.getvalue()).decode("utf-8")
66
+
67
+ return img_base64_str
68
+
69
+ @classmethod
70
+ def create_message(cls, images_data: List[str]) -> HumanMessage:
71
+ message = HumanMessage(
72
+ content=[
73
+ {
74
+ "type": "text",
75
+ "text": cls.prompt_instructions,
76
+ },
77
+ ]
78
+ + [
79
+ {
80
+ "type": "image_url",
81
+ "image_url": {"url": f"data:image/png;base64,{img_data}"},
82
+ }
83
+ for img_data in images_data
84
+ ],
85
+ )
86
+
87
+ return message
88
+
89
+ def extract_location_from_response(
90
+ self, response: BaseMessage
91
+ ) -> Tuple[float, float]:
92
+ try:
93
+ response = response.content.split("\n")
94
+ while (
95
+ response
96
+ and len(response[-1]) == 0
97
+ and "lat" not in response[-1].lower()
98
+ ):
99
+ response.pop()
100
+ if response:
101
+ prediction = response[-1]
102
+ else:
103
+ return None
104
+ print(f"\n-------\n{self.model} Prediction:\n", prediction)
105
+
106
+ # Lat: 57.7916, Lon: -152.4083
107
+ lat = float(prediction.split(",")[0].split(":")[1])
108
+ lon = float(prediction.split(",")[1].split(":")[1])
109
+
110
+ x, y = self.lat_lon_to_mercator_map_pixels(lat, lon)
111
+ print(f"Normalized pixel coordinates: ({x}, {y})")
112
+
113
+ if x < self.map_x:
114
+ x = self.map_x
115
+ print("x out of bounds")
116
+ elif x > self.map_x + self.map_w:
117
+ x = self.map_x + self.map_w
118
+ print("x out of bounds")
119
+ if y < self.map_y:
120
+ y = self.map_y
121
+ print("y out of bounds")
122
+ elif y > self.map_y + self.map_h:
123
+ y = self.map_y + self.map_h
124
+ print("y out of bounds")
125
+
126
+ return x, y
127
+
128
+ except Exception as e:
129
+ print("Error:", e)
130
+ return None
131
+
132
+ @staticmethod
133
+ def lat_to_mercator_y(lat: float) -> float:
134
+ return math.log(math.tan(math.pi / 4 + math.radians(lat) / 2))
135
+
136
+ def lat_lon_to_mercator_map_pixels(self, lat: float, lon: float) -> Tuple[int, int]:
137
+ """
138
+ Convert latitude and longitude to pixel coordinates on the mercator projection minimap,
139
+ taking two known points 1 and 2 as a reference.
140
+
141
+ Args:
142
+ lat (float): Latitude (Decimal Degrees) of the point to convert.
143
+ lon (float): Longitude (Decimal Degrees) of the point to convert.
144
+
145
+ Returns:
146
+ tuple: x, y pixel coordinates of the point.
147
+ """
148
+
149
+ # Calculate the x pixel coordinate
150
+ lon_diff_ref = self.kodiak_lon - self.hobart_lon
151
+ lon_diff = self.kodiak_lon - lon
152
+
153
+ x = (
154
+ abs(self.kodiak_x - self.hobart_x) * (lon_diff / lon_diff_ref)
155
+ + self.kodiak_x
156
+ )
157
+
158
+ # Convert latitude and longitude to mercator projection y coordinates
159
+ mercator_y1 = self.lat_to_mercator_y(self.kodiak_lat)
160
+ mercator_y2 = self.lat_to_mercator_y(self.hobart_lat)
161
+ mercator_y = self.lat_to_mercator_y(lat)
162
+
163
+ # Calculate the y pixel coordinate
164
+ lat_diff_ref = mercator_y1 - mercator_y2
165
+ lat_diff = mercator_y1 - mercator_y
166
+
167
+ y = (
168
+ abs(self.kodiak_y - self.hobart_y) * (lat_diff / lat_diff_ref)
169
+ + self.kodiak_y
170
+ )
171
+
172
+ return round(x), round(y)
173
+
174
+ def select_map_location(self, x: int, y: int, plot: bool = False) -> None:
175
+ # Hovering over the minimap to expand it
176
+ pyautogui.moveTo(
177
+ self.map_x + self.map_w - 15, self.map_y + self.map_h - 15, duration=0.5
178
+ )
179
+ # bot.screen_w-50, bot.screen_h-80
180
+ # pyautogui.moveTo(self.screen_w-50, self.screen_h-80, duration=1.5)
181
+ # print(self.screen_w-50, self.screen_h-80)
182
+ print("finish moving")
183
+ sleep(0.5)
184
+
185
+ # Clicking on the predicted location
186
+ pyautogui.click(x, y, duration=0.5)
187
+ print("finish clicking")
188
+ sleep(0.5)
189
+
190
+ if plot:
191
+ self.plot_minimap(x, y)
192
+
193
+ # Confirming the guessed location
194
+ pyautogui.click(self.confirm_button, duration=0.2)
195
+ sleep(2)
196
+
197
+ def plot_minimap(self, x: int = None, y: int = None) -> None:
198
+ minimap = pyautogui.screenshot(region=self.minimap_xywh)
199
+ plot_kodiak_x = self.kodiak_x - self.map_x
200
+ plot_kodiak_y = self.kodiak_y - self.map_y
201
+ plot_hobart_x = self.hobart_x - self.map_x
202
+ plot_hobart_y = self.hobart_y - self.map_y
203
+ plt.imshow(minimap)
204
+ plt.plot(plot_hobart_x, plot_hobart_y, "ro")
205
+ plt.plot(plot_kodiak_x, plot_kodiak_y, "ro")
206
+ if x and y:
207
+ plt.plot(x - self.map_x, y - self.map_y, "bo")
208
+
209
+ os.makedirs("plots", exist_ok=True)
210
+ plt.savefig("plots/minimap.png")
211
+ # plt.show()