ai-snake-lab 0.1.0__py3-none-any.whl → 0.4.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,160 @@
1
+ """
2
+ db4e/Modules/Db4EPlot.py
3
+
4
+ Database 4 Everything
5
+ Author: Nadim-Daniel Ghaznavi
6
+ Copyright: (c) 2024-2025 Nadim-Daniel Ghaznavi
7
+ GitHub: https://github.com/NadimGhaznavi/db4e
8
+ License: GPL 3.0
9
+ """
10
+
11
+ import math
12
+ from collections import deque
13
+ from textual_plot import PlotWidget, HiResMode, LegendLocation
14
+ from textual.app import ComposeResult
15
+
16
+ from ai_snake_lab.constants.DDb4EPlot import Plot
17
+ from ai_snake_lab.constants.DLabels import DLabel
18
+
19
+
20
+ MAX_DATA_POINTS = Plot.MAX_DATA_POINTS
21
+
22
+
23
+ class Db4EPlot(PlotWidget):
24
+ """
25
+ A widget for plotting data based on TextualPlot's PlotWidget.
26
+ """
27
+
28
+ def __init__(self, title, id, thin_method=None):
29
+ super().__init__(title, id, allow_pan_and_zoom=False)
30
+ self._plot_id = id
31
+ self._title = title
32
+ self._thin_method = thin_method
33
+ if thin_method == Plot.SLIDING:
34
+ self._all_days = deque(maxlen=MAX_DATA_POINTS)
35
+ self._all_values = deque(maxlen=MAX_DATA_POINTS)
36
+ else:
37
+ self._all_days = None
38
+ self._all_values = None
39
+
40
+ def compose(self) -> ComposeResult:
41
+ yield PlotWidget()
42
+
43
+ def load_data(self, days, values, units):
44
+ self._all_days = days
45
+ self._all_values = values
46
+ if units:
47
+ self.set_ylabel(self._title + " (" + units + ")")
48
+ else:
49
+ self.set_ylabel(self._title)
50
+
51
+ def add_data(self, day, value):
52
+ self._all_days.append(day)
53
+ self._all_values.append(value)
54
+
55
+ def set_xlabel(self, label):
56
+ return super().set_xlabel(label)
57
+
58
+ def db4e_plot(self, days=None, values=None) -> None:
59
+ if days is not None and values is not None:
60
+ plot_days = days
61
+ plot_values = values
62
+ else:
63
+ plot_days = self._all_days
64
+ plot_values = self._all_values
65
+ self.clear()
66
+ if len(plot_days) == 0:
67
+ return
68
+ if self._thin_method == Plot.AVERAGE:
69
+ reduced_days, reduced_values = self.reduce_data(plot_days, plot_values)
70
+ else:
71
+ reduced_days, reduced_values = list(self._all_days), list(self._all_values)
72
+
73
+ self.plot(
74
+ x=reduced_days,
75
+ y=reduced_values,
76
+ hires_mode=HiResMode.BRAILLE,
77
+ line_style="green",
78
+ label=DLabel.CURRENT,
79
+ )
80
+
81
+ # Add an average plot over 20 to wash out the spikes and identify when the
82
+ # AI is maxing out.
83
+ window = max(1, len(reduced_values) // 20) # e.g., 5% smoothing window
84
+ if len(reduced_values) > window:
85
+ smoothed = [
86
+ sum(reduced_values[i : i + window])
87
+ / len(reduced_values[i : i + window])
88
+ for i in range(len(reduced_values) - window + 1)
89
+ ]
90
+ smoothed_days = reduced_days[window - 1 :]
91
+ self.plot(
92
+ x=smoothed_days,
93
+ y=smoothed,
94
+ hires_mode=HiResMode.BRAILLE,
95
+ line_style="red", # distinct color for trend
96
+ label=DLabel.AVERAGE,
97
+ )
98
+ self.show_legend(location=LegendLocation.TOPLEFT)
99
+
100
+ def reduce_data2(self, times, values):
101
+ # Reduce the total number of data points, otherwise the plot gets "blurry"
102
+ step = max(1, len(times) // MAX_DATA_POINTS)
103
+
104
+ # Reduce times with step
105
+ reduced_times = times[::step]
106
+
107
+ # Bin values by step (average)
108
+ reduced_values = [
109
+ sum(values[i : i + step]) / len(values[i : i + step])
110
+ for i in range(0, len(values), step)
111
+ ]
112
+ results = reduced_times[: len(reduced_values)], reduced_values
113
+ return results
114
+
115
+ def reduce_data(self, times, values):
116
+ """Reduce times and values into <= MAX_DATA_POINTS bins.
117
+ Each bin's value is the average of the values in the bin.
118
+ Each bin's time is chosen as the last time in the bin (so last bin -> times[-1]).
119
+ """
120
+ if not times or not values:
121
+ return [], []
122
+
123
+ assert len(times) == len(values), "times and values must be same length"
124
+
125
+ step = max(1, math.ceil(len(times) / MAX_DATA_POINTS))
126
+
127
+ reduced_times = []
128
+ reduced_values = []
129
+ for i in range(0, len(times), step):
130
+ chunk_times = times[i : i + step]
131
+ chunk_vals = values[i : i + step]
132
+
133
+ # average values (works for floats or Decimal)
134
+ avg_val = sum(chunk_vals) / len(chunk_vals)
135
+
136
+ # representative time: choose last item in the chunk so final rep is times[-1]
137
+ rep_time = chunk_times[-1]
138
+
139
+ reduced_times.append(rep_time)
140
+ reduced_values.append(avg_val)
141
+
142
+ # Guarantee the final time equals the exact last time (safety)
143
+ if reduced_times:
144
+ reduced_times[-1] = times[-1]
145
+
146
+ return reduced_times, reduced_values
147
+
148
+ def update_time_range(self, selected_time):
149
+ if selected_time == -1:
150
+ return
151
+
152
+ selected_time = int(selected_time)
153
+ max_length = len(self._all_days)
154
+ if selected_time > max_length:
155
+ new_values = self._all_values
156
+ new_times = self._all_days
157
+ else:
158
+ new_values = self._all_values[-selected_time:]
159
+ new_times = self._all_days[-selected_time:]
160
+ self.db4e_plot(new_times, new_values)
@@ -1,21 +1,30 @@
1
- Button {
2
- align: center middle;
3
- border: round #0c323e;
4
- width: 10;
5
- background: black;
1
+ Screen {
2
+ layout: grid;
3
+ grid-size: 3 4;
4
+ grid-rows: 3 7 6 11 10;
5
+ grid-columns: 32 46 30;
6
6
  }
7
-
8
- #game_board {
9
- height: 22;
7
+
8
+ #title {
9
+ column-span: 3;
10
+ width: 100%;
11
+ padding: 0 1;
10
12
  border: round #0c323e;
11
- width: 42;
12
- align: center middle;
13
+ color: #3e99af;
14
+ background: black;
15
+ text-style: bold;
16
+ align: center middle; }
17
+
18
+ #settings_box {
19
+ border-title-color: #5fc442;
20
+ border-title-style: bold;
21
+ border: round #0c323e;
22
+ padding: 0 1;
13
23
  background: black;
14
24
  }
15
25
 
16
26
  #game_box {
17
- height: 24;
18
- width: 46;
27
+ row-span: 3;
19
28
  border-title-color: #5fc442;
20
29
  border-subtitle-color: #5fc442;
21
30
  border: round #0c323e;
@@ -25,7 +34,6 @@ Button {
25
34
  }
26
35
 
27
36
  #runtime_box {
28
- height: 24;
29
37
  border-title-color: #5fc442;
30
38
  border-title-style: bold;
31
39
  border: round #0c323e;
@@ -33,40 +41,48 @@ Button {
33
41
  background: black;
34
42
  }
35
43
 
36
- #score {
37
- height: 3;
44
+ #filler_1 {
45
+ }
46
+
47
+ #filler_2 {
48
+ }
49
+
50
+ #filler_3 {
51
+ }
52
+
53
+ #game_score_plot {
54
+ dock: bottom;
38
55
  border: round #0c323e;
39
- width: 42;
40
- padding: 0 1;
41
- color: #5fc442;
42
- background: black;
56
+ height: 15;
57
+ width: 108;
58
+ background: black
43
59
  }
44
60
 
45
- #settings_box {
46
- height: 6;
47
- border-title-color: #5fc442;
48
- border-title-style: bold;
61
+ #legend {
49
62
  border: round #0c323e;
50
- padding: 0 1;
51
- background: black;
52
63
  }
53
64
 
54
- #title {
55
- height: 3;
56
- padding: 0 1;
65
+ Button {
66
+ align: center middle;
57
67
  border: round #0c323e;
58
- color: #3e99af;
59
- width: 100%;
68
+ width: 10;
69
+ background: black;
70
+ }
71
+
72
+ #game_board {
73
+ border: round #0c323e;
74
+ align: center middle;
60
75
  background: black;
61
- text-style: bold;
62
- align: center middle; }
76
+ }
63
77
 
64
- #button_pause {
65
- color: #c44242;
78
+
79
+
80
+ #button_defaults {
81
+ color: #3e99af;
66
82
  text-style: bold;
67
83
  }
68
84
 
69
- #button_reset {
85
+ #button_pause {
70
86
  color: #c49b42;
71
87
  text-style: bold;
72
88
  }
@@ -76,6 +92,11 @@ Button {
76
92
  text-style: bold;
77
93
  }
78
94
 
95
+ #button_restart {
96
+ color: #3e99af;
97
+ text-style: bold;
98
+ }
99
+
79
100
  #button_quit {
80
101
  color: #c44242;
81
102
  text-style: bold;
@@ -91,15 +112,37 @@ Button {
91
112
  }
92
113
 
93
114
  .label {
115
+ width: 15;
94
116
  color: #afa73eff;
95
117
  }
96
118
 
97
- .running #button_start {
119
+ .label_settings {
120
+ color: #5fc442;
121
+ width: 18;
122
+ }
123
+
124
+ .paused #button_pause {
98
125
  display: none;
99
126
  }
100
127
 
101
- .label_settings {
102
- color: #5fc442;
128
+ .paused #button_update {
129
+ display: none;
130
+ }
131
+
132
+ .running #button_reset {
133
+ display: none;
134
+ }
135
+
136
+ .running #button_restart {
137
+ display: none;
138
+ }
139
+
140
+ .stopped #button_restart {
141
+ display: none;
142
+ }
143
+
144
+ .running #button_start {
145
+ display: none;
103
146
  }
104
147
 
105
148
  .stopped #button_pause {
@@ -1,8 +1,9 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: ai-snake-lab
3
- Version: 0.1.0
3
+ Version: 0.4.4
4
4
  Summary: Interactive reinforcement learning sandbox for experimenting with AI agents in a classic Snake Game environment.
5
5
  License: GPL-3.0
6
+ License-File: LICENSE
6
7
  Keywords: AI,Reinforcement Learning,Textual,Snake,Simulation,SQLite,Python
7
8
  Author: Nadim-Daniel Ghaznavi
8
9
  Author-email: nghaznavi@gmail.com
@@ -63,8 +64,41 @@ Description-Content-Type: text/markdown
63
64
 
64
65
  ---
65
66
 
66
- # Links
67
+ # Installation
68
+
69
+ This project is on [PyPI](https://pypi.org/project/ai-snake-lab/). You can install the *AI Snake Lab* software using `pip`.
70
+
71
+ ## Create a Sandbox
72
+
73
+ ```shell
74
+ python3 -m venv snake_venv
75
+ . snake_venv/bin/activate
76
+ ```
77
+
78
+ ## Install the AI Snake Lab
79
+
80
+ After you have activated your *venv* environment:
81
+
82
+ ```shell
83
+ pip install ai-snake-lab
84
+ ```
85
+
86
+ ---
87
+
88
+ # Running the AI Snake Lab
89
+
90
+ From within your *venv* environment:
91
+
92
+ ```shell
93
+ ai-snake-lab
94
+ ```
95
+
96
+ ---
97
+
98
+ # Links and Acknowledgements
99
+
100
+ This code is based on a YouTube tutorial, [Python + PyTorch + Pygame Reinforcement Learning – Train an AI to Play Snake](https://www.youtube.com/watch?v=L8ypSXwyBds&t=1042s&ab_channel=freeCodeCamp.org) by Patrick Loeber. You can access his original code [here](https://github.com/patrickloeber/snake-ai-pytorch) on GitHub. Thank you Patrick!!! You are amazing!!!!
101
+
102
+ Thanks also go out to Will McGugan and the [Textual](https://textual.textualize.io/) team. Textual is an amazing framework. Talk about *rapid Application Development*. Porting this took less than a day.
67
103
 
68
- * [Project Layout](/pages/project_layout.html)
69
- * [SQLite3 Schema](/pages/db_schema.html)
70
104
  ---
@@ -0,0 +1,31 @@
1
+ ai_snake_lab/AISim.py,sha256=GiqzvU0LAvjjmjP_0JDeak7DAF0R-pEzrPgVpCN-gs8,15148
2
+ ai_snake_lab/ai/AIAgent.py,sha256=p_O7zC15s_tBcI7oqu60X2-3AbrzAWlGXWkrLVC4UsQ,3005
3
+ ai_snake_lab/ai/AITrainer.py,sha256=ssX6B03yZLEKhNCJv9D83iFAEEhU3_XbW4sA0z5QMRM,3304
4
+ ai_snake_lab/ai/EpsilonAlgo.py,sha256=iNVXOqlyjh5vlFePpksR0PTT9M5FDDwJi7P6pYegSrA,2144
5
+ ai_snake_lab/ai/ReplayMemory.py,sha256=eGdszKKeYuvppmmkA9AtypLRbGZQGZO-uCSHb8qcesw,3986
6
+ ai_snake_lab/ai/models/ModelL.py,sha256=hK7MoPyIF1_K43R_9xW_MYuaweQGo9qSMUIVrHDQZnQ,1249
7
+ ai_snake_lab/ai/models/ModelRNN.py,sha256=Ky63BUqJEmwe1PbM4gtjvqd7SmSy_2XA1n-yu8DNexI,1104
8
+ ai_snake_lab/constants/DDb4EPlot.py,sha256=2g8SdDVZWrTVSTw-rpO84OrNfl7ToBYtf6J7sYB7q8w,442
9
+ ai_snake_lab/constants/DDef.py,sha256=XABLL9P_WUkCiNr2L-phiNL6cnxc_E0uD4vW6WhtZs0,378
10
+ ai_snake_lab/constants/DDir.py,sha256=e8pNvZJpR4R6Cm3VRGljry_g-GAODYkz1ZrU8OudXuQ,395
11
+ ai_snake_lab/constants/DEpsilon.py,sha256=HuhMGxy3WIebcFQ2XZvlOpl03JwPYhHZdO40Bzph9yM,420
12
+ ai_snake_lab/constants/DFields.py,sha256=0MV21hLy-Ojnn4Qa3C9_dQLPjC-3VYBjes_YU5V1Bio,521
13
+ ai_snake_lab/constants/DFile.py,sha256=jYnYgrMqw3BMa3XEJw5XtC5buHfE0v7QFLv25BKNHW0,378
14
+ ai_snake_lab/constants/DLabels.py,sha256=Nv7vCs8pxpF6Px5FD3JFkI7KEPWy6G8wLqzv31_nXxw,1501
15
+ ai_snake_lab/constants/DLayout.py,sha256=XhRMpOqgzOkL0kcFz4pfS-HuVsYZZLBC6GIAq-E1_Ik,1616
16
+ ai_snake_lab/constants/DModelL.py,sha256=VzAK5Uqkb0ovMPKTo_DJ2lQmIJkpRQyF-z3g3y4-j20,506
17
+ ai_snake_lab/constants/DModelLRNN.py,sha256=nyPYa1-8JB7KERWEJM5jBFH3PmISNcr9JPi-KJ_Cq2g,445
18
+ ai_snake_lab/constants/DReplayMemory.py,sha256=m0SSxFL6v2mVXVxf0lJTJzKfXguxrnsnrJph0v-2lX8,589
19
+ ai_snake_lab/constants/DSim.py,sha256=ULi_e9g2eBPPZv_swQQoagKLT8dL3gJTwlQihkQk8RY,510
20
+ ai_snake_lab/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ ai_snake_lab/game/GameBoard.py,sha256=VjjNXnZZJ_lTo4bcxflB9TWxi792Do7yMnvL3umrMCc,8452
22
+ ai_snake_lab/game/GameElements.py,sha256=9vcNH2YWAzYyBZosvj1DzO6ZmUCUMUd8MaIhORWQ8go,470
23
+ ai_snake_lab/game/SnakeGame.py,sha256=_gyxhLPggPF7B2XvmWLgWP2ywnYWX7QjmwLmi21QuIw,6476
24
+ ai_snake_lab/ui/Db4EPlot.py,sha256=pcEb0ydXNX2wL0EFBSrqhIoTEhryk4GD0Ua8FFEaZHY,5352
25
+ ai_snake_lab/utils/AISim.tcss,sha256=XnhqjuxtOiNQHDyXFrbAYSlCw-6ch4e1o52nbytoeF0,2118
26
+ ai_snake_lab/utils/ConstGroup.py,sha256=ZYyQxFd9PudBUZmc_NsNvWCp___utOe1MptqD3eyVH8,1174
27
+ ai_snake_lab-0.4.4.dist-info/METADATA,sha256=dNvC3Ia4TfGeL5L4N6rS2snjbydNdT_ZCo1XGyYGSMU,3689
28
+ ai_snake_lab-0.4.4.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
29
+ ai_snake_lab-0.4.4.dist-info/entry_points.txt,sha256=VXWDqigE8D35J7scdqjR0EVvOjtP1mm0uS76xMnvodo,56
30
+ ai_snake_lab-0.4.4.dist-info/licenses/LICENSE,sha256=f-FHFu0xzHH8O_mvKTw2jUZKhTpw6obpmVOI9rnpKeU,35151
31
+ ai_snake_lab-0.4.4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -672,3 +672,5 @@ may consider it more useful to permit linking proprietary applications with
672
672
  the library. If this is what you want to do, use the GNU Lesser General
673
673
  Public License instead of this License. But first, please read
674
674
  <https://www.gnu.org/licenses/why-not-lgpl.html>.
675
+
676
+
@@ -1,28 +0,0 @@
1
- ai_snake_lab/AISim.py,sha256=9jnWX-JYNqpYxtqb7vf4pbI9tgegrt91QnFWfVU2AFI,10168
2
- ai_snake_lab/ai/AIAgent.py,sha256=mwfDwQlm9vRCDlXj5LyIQGtwjZL3j4Fc_L9w79jm2wA,2955
3
- ai_snake_lab/ai/AITrainer.py,sha256=VbLfSV3ndplG-KhqpTlUSkoH8lFGGT0auaPjFRzabR0,3169
4
- ai_snake_lab/ai/EpsilonAlgo.py,sha256=iNVXOqlyjh5vlFePpksR0PTT9M5FDDwJi7P6pYegSrA,2144
5
- ai_snake_lab/ai/ReplayMemory.py,sha256=g8Q_PXiFSL1g4zOEa8pKc9w_x5ijVlG17npYpawL_14,2864
6
- ai_snake_lab/ai/models/ModelL.py,sha256=EiTGVag8Sxhoak1sklA4iX4FR65Nlx8m9o-JQNAqOX8,1096
7
- ai_snake_lab/ai/models/ModelRNN.py,sha256=jIPK5ZS1olakjQ-9XA6-4EiY70d3RLcKqFqHTZD7cIQ,1104
8
- ai_snake_lab/constants/DDef.py,sha256=zyvRkWPrtopKYclbbDGI-1pPAK1h1MyVMdbvhUZPbJY,371
9
- ai_snake_lab/constants/DDir.py,sha256=AxmgShKHT5b5WyIlseVHcO7Mwejmyf3hxT62E3RirTg,323
10
- ai_snake_lab/constants/DEpsilon.py,sha256=T4B_KMHrCEKsiCx2AgvnXMpQ-V4D2oSCMPkYqw2d-0U,407
11
- ai_snake_lab/constants/DFields.py,sha256=Z_whswaGfNIRe9d_9UeXWiWZCB0d48BpM1Wg6tILEa0,357
12
- ai_snake_lab/constants/DFile.py,sha256=i6i_K3y90-GgqesN9_zL2NmQu0Vu2VT2V5k7n6QFQR4,328
13
- ai_snake_lab/constants/DLabels.py,sha256=5kUrAbYgdJL9zynAjSFQr3FmEvy2CbU0DKRZfwUs71I,883
14
- ai_snake_lab/constants/DLayout.py,sha256=T5CeJBaiDST1gaX3ek6TpRJdgETvbl4uk-tlWZah-Y8,1125
15
- ai_snake_lab/constants/DModelL.py,sha256=-bb1SMQQE-iGxZcuAGcJv-5xJqtZKsYbmMHwUak11jg,351
16
- ai_snake_lab/constants/DModelLRNN.py,sha256=gceyvn0-I3XpRL6cUb8Ep67DoybN8bZdDpcXVKf_DCk,432
17
- ai_snake_lab/constants/DReplayMemory.py,sha256=DNPn9dFa9piNXpuT6AMx1B96UpoMFXALrYYLjyKwPpU,576
18
- ai_snake_lab/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- ai_snake_lab/game/GameBoard.py,sha256=wl2V0cxjqBKHuS1DSx36EE_DRH54tikuhaA9eC5qPsY,7857
20
- ai_snake_lab/game/GameElements.py,sha256=9vcNH2YWAzYyBZosvj1DzO6ZmUCUMUd8MaIhORWQ8go,470
21
- ai_snake_lab/game/SnakeGame.py,sha256=1YS0xgyzuGQtGgKxDUcPJYM2NV56ekL4OpaOrbpnDW0,5673
22
- ai_snake_lab/utils/AISim.tcss,sha256=c0Xq8hjA7YokioZUzJGHhAlX13t2kgelA26hOWtUSVQ,1674
23
- ai_snake_lab/utils/ConstGroup.py,sha256=ZYyQxFd9PudBUZmc_NsNvWCp___utOe1MptqD3eyVH8,1174
24
- ai_snake_lab-0.1.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
25
- ai_snake_lab-0.1.0.dist-info/METADATA,sha256=X3xDSODxx3q8zlm8GTntrkCa7PL7AbGm-QsDqFgTj8A,2712
26
- ai_snake_lab-0.1.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
27
- ai_snake_lab-0.1.0.dist-info/entry_points.txt,sha256=VXWDqigE8D35J7scdqjR0EVvOjtP1mm0uS76xMnvodo,56
28
- ai_snake_lab-0.1.0.dist-info/RECORD,,