furuknap commited on
Commit
8ceaa24
·
verified ·
1 Parent(s): ad7eaf6

I want to create a simple horse breeding, training, and racing game. The game loop is to buy horses and breed them. Each breeding has a chance of getting a trait, positive or negative, from the parents. Horses have four stats: - Speed - Endurance - Beauty - Fertility In addition, a horse has a gender. These stats have a genetic value and a modified value. The genetic value is what can be obtained by breeding. In addition, traits can modify stats. For example, a "Slow but steady" trait can decrease speed but increase endurance. Training can be done three times between races. Training has a chance of injuring the horse, in which case they cannot participate in the next race. In races, only speed and endurance are used. During the race, each horse has a temporary endurance and speed value that starts at the endurance and speed values. The temporary endurance is decreased by the temporary speed value each lap. When the temporary endurance reaches 50%, the speed reduces by 25%. When the temporary endurance reaches 25%, the speed reduces by another 25%, and when the temporary endurance reaches <10%, the speed reduces to 10% too, and no further reduction in endurance or speed happens until the end of the race. For breeding, each horse has a fertility that starts at 100% and decreases by 25% each time it is bred. The chance that its genetic value and traits are passed on depends on the remaining fertility. When fertility reaches 0%, the horse can no longer breed. Beauty is not required for the first version of the game. The player starts with $10K and must buy a horse on a market. There will be three horses each round that the player can purchase, of various values. At least one horse is always available below the player's remaining money. The cost of a horse should be a reflection of its stats. The player can earn more money by winning races or selling offspring or breeding services. The value of offspring and breeding services depends on the stats. The value should in total yield around 1/4 of the value of the horse. When choosing breeding, a player can also breed with their own horses if they have eligible male and female hoses. If the player has a female horse only, they must pay someone to mate, around 1/4 of the male horse's value, but the player gets offspring in return. If the player has a male horse only, they immediately get paid around 1/4 of the value of their horse. - Initial Deployment

Browse files
Files changed (3) hide show
  1. README.md +6 -4
  2. index.html +1712 -19
  3. prompts.txt +1 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Stablemasters
3
- emoji:
4
  colorFrom: blue
5
- colorTo: yellow
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: stablemasters
3
+ emoji: 🐳
4
  colorFrom: blue
5
+ colorTo: purple
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1712 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Stable Masters - Horse Breeding & Racing</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ @keyframes raceHorse {
11
+ 0% { transform: translateX(0); }
12
+ 100% { transform: translateX(calc(100% - 100px)); }
13
+ }
14
+ .race-track {
15
+ height: 60px;
16
+ background-image: repeating-linear-gradient(
17
+ 90deg,
18
+ #f0fdf4,
19
+ #f0fdf4 40px,
20
+ #bbf7d0 40px,
21
+ #bbf7d0 80px
22
+ );
23
+ position: relative;
24
+ }
25
+ .horse-runner {
26
+ position: absolute;
27
+ left: 0;
28
+ animation: raceHorse linear forwards;
29
+ height: 50px;
30
+ width: 80px;
31
+ background-size: contain;
32
+ background-repeat: no-repeat;
33
+ filter: drop-shadow(0 0 2px rgba(0,0,0,0.5));
34
+ }
35
+ .stats-bar {
36
+ height: 8px;
37
+ border-radius: 4px;
38
+ }
39
+ /* Horse gender colors */
40
+ .horse-male {
41
+ background-color: #93c5fd;
42
+ }
43
+ .horse-female {
44
+ background-color: #f9a8d4;
45
+ }
46
+ /* Stat colors */
47
+ .stat-speed {
48
+ background-color: #ef4444;
49
+ }
50
+ .stat-endurance {
51
+ background-color: #3b82f6;
52
+ }
53
+ .stat-beauty {
54
+ background-color: #8b5cf6;
55
+ }
56
+ .stat-fertility {
57
+ background-color: #10b981;
58
+ }
59
+ /* Modal styles */
60
+ .game-modal {
61
+ display: none;
62
+ position: fixed;
63
+ top: 0;
64
+ left: 0;
65
+ width: 100%;
66
+ height: 100%;
67
+ background-color: rgba(0,0,0,0.7);
68
+ z-index: 1000;
69
+ justify-content: center;
70
+ align-items: center;
71
+ }
72
+ .modal-content {
73
+ background-color: white;
74
+ border-radius: 0.5rem;
75
+ padding: 1.5rem;
76
+ max-width: 90%;
77
+ max-height: 90vh;
78
+ overflow-y: auto;
79
+ }
80
+ /* Tooltip styles */
81
+ .tooltip {
82
+ position: relative;
83
+ display: inline-block;
84
+ }
85
+ .tooltip .tooltip-text {
86
+ visibility: hidden;
87
+ width: 200px;
88
+ background-color: #333;
89
+ color: #fff;
90
+ text-align: center;
91
+ border-radius: 6px;
92
+ padding: 5px;
93
+ position: absolute;
94
+ z-index: 1;
95
+ bottom: 125%;
96
+ left: 50%;
97
+ transform: translateX(-50%);
98
+ opacity: 0;
99
+ transition: opacity 0.3s;
100
+ }
101
+ .tooltip:hover .tooltip-text {
102
+ visibility: visible;
103
+ opacity: 1;
104
+ }
105
+ </style>
106
+ </head>
107
+ <body class="bg-green-50 min-h-screen">
108
+ <div id="game-container" class="container mx-auto px-4 py-8">
109
+ <!-- Game Header -->
110
+ <header class="mb-8 text-center">
111
+ <h1 class="text-4xl font-bold text-green-800 mb-2">Stable Masters</h1>
112
+ <p class="text-lg text-green-600 mb-4">Breed. Train. Race. Win.</p>
113
+ <div class="bg-white rounded-lg shadow-md p-4 flex justify-between items-center mb-6">
114
+ <div id="money-display" class="text-2xl font-bold text-yellow-600">
115
+ <i class="fas fa-coins mr-2"></i> $10,000
116
+ </div>
117
+ <div id="day-display" class="text-xl font-semibold text-gray-700">
118
+ <i class="far fa-calendar-alt mr-2"></i> Day 1
119
+ </div>
120
+ <button id="next-day-btn" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded transition">
121
+ <i class="fas fa-calendar-plus mr-2"></i>Next Day
122
+ </button>
123
+ </div>
124
+ </header>
125
+
126
+ <!-- Main Game Area -->
127
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
128
+ <!-- Left Panel - Stable -->
129
+ <div id="stable-panel" class="bg-white rounded-lg shadow-md p-4">
130
+ <h2 class="text-2xl font-bold text-green-800 mb-4 border-b-2 pb-2 flex justify-between">
131
+ <span><i class="fas fa-horse-head mr-2"></i>Your Stable</span>
132
+ <span id="stable-count" class="text-gray-600">0 horses</span>
133
+ </h2>
134
+ <div id="stable-horses" class="space-y-4">
135
+ <p class="text-gray-500 italic">No horses in your stable yet. Visit the market to buy your first horse!</p>
136
+ </div>
137
+ </div>
138
+
139
+ <!-- Center Panel - Actions -->
140
+ <div id="actions-panel" class="bg-white rounded-lg shadow-md p-4">
141
+ <h2 class="text-2xl font-bold text-green-800 mb-4 border-b-2 pb-2">
142
+ <i class="fas fa-tasks mr-2"></i>Actions
143
+ </h2>
144
+ <div class="space-y-4">
145
+ <button id="market-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded transition flex items-center justify-between">
146
+ <span><i class="fas fa-shopping-cart mr-2"></i>Visit Market</span>
147
+ <i class="fas fa-arrow-right"></i>
148
+ </button>
149
+ <button id="breed-btn" disabled class="w-full bg-purple-600 hover:bg-purple-700 text-white font-bold py-3 px-4 rounded transition flex items-center justify-between opacity-50 cursor-not-allowed">
150
+ <span><i class="fas fa-dna mr-2"></i>Breed Horses</span>
151
+ <i class="fas fa-arrow-right"></i>
152
+ </button>
153
+ <button id="train-btn" disabled class="w-full bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-3 px-4 rounded transition flex items-center justify-between opacity-50 cursor-not-allowed">
154
+ <span><i class="fas fa-dumbbell mr-2"></i>Train Horses</span>
155
+ <i class="fas fa-arrow-right"></i>
156
+ </button>
157
+ <button id="race-btn" disabled class="w-full bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-4 rounded transition flex items-center justify-between opacity-50 cursor-not-allowed">
158
+ <span><i class="fas fa-flag-checkered mr-2"></i>Enter Race</span>
159
+ <i class="fas fa-arrow-right"></i>
160
+ </button>
161
+ <button id="sell-btn" disabled class="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded transition flex items-center justify-between opacity-50 cursor-not-allowed">
162
+ <span><i class="fas fa-hand-holding-usd mr-2"></i>Sell Horses</span>
163
+ <i class="fas fa-arrow-right"></i>
164
+ </button>
165
+ </div>
166
+ </div>
167
+
168
+ <!-- Right Panel - Racing & Events -->
169
+ <div id="events-panel" class="bg-white rounded-lg shadow-md p-4">
170
+ <h2 class="text-2xl font-bold text-green-800 mb-4 border-b-2 pb-2">
171
+ <i class="fas fa-trophy mr-2"></i>Racing & Events
172
+ </h2>
173
+ <div class="space-y-6">
174
+ <div id="upcoming-races">
175
+ <h3 class="text-lg font-semibold text-gray-700 mb-2">Upcoming Races</h3>
176
+ <div class="bg-gray-100 p-3 rounded-lg">
177
+ <p class="text-gray-700">No races scheduled yet.</p>
178
+ </div>
179
+ </div>
180
+ <div id="race-results" class="hidden">
181
+ <h3 class="text-lg font-semibold text-gray-700 mb-2">Race Results</h3>
182
+ <div class="bg-gray-100 p-3 rounded-lg">
183
+ <div id="race-track" class="race-track mb-4">
184
+ <!-- Horses will be added here dynamically -->
185
+ </div>
186
+ <div id="race-results-list" class="space-y-2">
187
+ <!-- Results will be added here -->
188
+ </div>
189
+ </div>
190
+ </div>
191
+ <div id="game-messages">
192
+ <h3 class="text-lg font-semibold text-gray-700 mb-2">Game Messages</h3>
193
+ <div class="bg-gray-100 p-3 rounded-lg h-48 overflow-y-auto">
194
+ <p class="text-gray-700">Welcome to Stable Masters! Buy your first horse to get started.</p>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ </div>
199
+ </div>
200
+ </div>
201
+
202
+ <!-- Market Modal -->
203
+ <div id="market-modal" class="game-modal">
204
+ <div class="modal-content w-11/12 lg:w-3/4 bg-white">
205
+ <div class="flex justify-between items-center mb-4">
206
+ <h2 class="text-2xl font-bold text-green-800">
207
+ <i class="fas fa-shopping-cart mr-2"></i>Horse Market
208
+ </h2>
209
+ <button id="close-market-btn" class="text-gray-500 hover:text-gray-700">
210
+ <i class="fas fa-times text-2xl"></i>
211
+ </button>
212
+ </div>
213
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4" id="market-horses">
214
+ <!-- Market horses will be added here -->
215
+ </div>
216
+ <div class="text-center text-gray-500 italic" id="no-horses-msg">
217
+ Loading horses available for purchase...
218
+ </div>
219
+ </div>
220
+ </div>
221
+
222
+ <!-- Breeding Modal -->
223
+ <div id="breed-modal" class="game-modal">
224
+ <div class="modal-content w-11/12 lg:w-3/4 bg-white">
225
+ <div class="flex justify-between items-center mb-4">
226
+ <h2 class="text-2xl font-bold text-purple-800">
227
+ <i class="fas fa-dna mr-2"></i>Breeding Center
228
+ </h2>
229
+ <button id="close-breed-btn" class="text-gray-500 hover:text-gray-700">
230
+ <i class="fas fa-times text-2xl"></i>
231
+ </button>
232
+ </div>
233
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
234
+ <div id="sires-list" class="border-2 border-gray-200 rounded-lg p-4">
235
+ <h3 class="text-lg font-semibold text-purple-700 mb-3">Select Sire (Male)</h3>
236
+ <div id="sire-options" class="space-y-3">
237
+ <!-- Male horses will be added here -->
238
+ </div>
239
+ </div>
240
+ <div id="dams-list" class="border-2 border-gray-200 rounded-lg p-4">
241
+ <h3 class="text-lg font-semibold text-purple-700 mb-3">Select Dam (Female)</h3>
242
+ <div id="dam-options" class="space-y-3">
243
+ <!-- Female horses will be added here -->
244
+ </div>
245
+ </div>
246
+ </div>
247
+ <div id="breeding-options" class="hidden">
248
+ <div class="bg-purple-100 p-4 rounded-lg mb-4">
249
+ <h3 class="text-lg font-semibold text-purple-800 mb-2">Breeding Preview</h3>
250
+ <div id="breeding-preview" class="grid grid-cols-1 md:grid-cols-2 gap-4">
251
+ <!-- Breeding preview will be shown here -->
252
+ </div>
253
+ </div>
254
+ <button id="confirm-breed-btn" class="w-full bg-purple-600 hover:bg-purple-700 text-white font-bold py-3 px-4 rounded transition">
255
+ <i class="fas fa-dna mr-2"></i>Confirm Breeding
256
+ </button>
257
+ </div>
258
+ <div id="external-breeding" class="hidden">
259
+ <div class="bg-yellow-100 p-4 rounded-lg">
260
+ <h3 class="text-lg font-semibold text-yellow-800 mb-2">External Breeding Options</h3>
261
+ <div id="external-options" class="space-y-3">
262
+ <!-- External breeding options will be shown here -->
263
+ </div>
264
+ </div>
265
+ </div>
266
+ </div>
267
+ </div>
268
+
269
+ <!-- Training Modal -->
270
+ <div id="train-modal" class="game-modal">
271
+ <div class="modal-content w-11/12 lg:w-3/4 bg-white">
272
+ <div class="flex justify-between items-center mb-4">
273
+ <h2 class="text-2xl font-bold text-yellow-800">
274
+ <i class="fas fa-dumbbell mr-2"></i>Training Grounds
275
+ </h2>
276
+ <button id="close-train-btn" class="text-gray-500 hover:text-gray-700">
277
+ <i class="fas fa-times text-2xl"></i>
278
+ </button>
279
+ </div>
280
+ <div class="mb-4">
281
+ <p class="text-gray-700">Select a horse to train. Each training session has a chance to increase stats but also a risk of injury.</p>
282
+ <p class="text-sm text-gray-500">Each horse can be trained 3 times between races.</p>
283
+ </div>
284
+ <div id="trainable-horses" class="space-y-3">
285
+ <!-- Trainable horses will be added here -->
286
+ </div>
287
+ </div>
288
+ </div>
289
+
290
+ <!-- Sell Modal -->
291
+ <div id="sell-modal" class="game-modal">
292
+ <div class="modal-content w-11/12 lg:w-3/4 bg-white">
293
+ <div class="flex justify-between items-center mb-4">
294
+ <h2 class="text-2xl font-bold text-indigo-800">
295
+ <i class="fas fa-hand-holding-usd mr-2"></i>Sell Horses
296
+ </h2>
297
+ <button id="close-sell-btn" class="text-gray-500 hover:text-gray-700">
298
+ <i class="fas fa-times text-2xl"></i>
299
+ </button>
300
+ </div>
301
+ <div class="mb-4">
302
+ <p class="text-gray-700">Select horses to sell for cash or offer breeding services for male horses.</p>
303
+ </div>
304
+ <div id="sellable-horses" class="space-y-3">
305
+ <!-- Sellable horses will be added here -->
306
+ </div>
307
+ </div>
308
+ </div>
309
+
310
+ <!-- Race Modal -->
311
+ <div id="race-modal" class="game-modal">
312
+ <div class="modal-content w-11/12 lg:w-3/4 bg-white">
313
+ <div class="flex justify-between items-center mb-4">
314
+ <h2 class="text-2xl font-bold text-red-800">
315
+ <i class="fas fa-flag-checkered mr-2"></i>Race Entry
316
+ </h2>
317
+ <button id="close-race-btn" class="text-gray-500 hover:text-gray-700">
318
+ <i class="fas fa-times text-2xl"></i>
319
+ </button>
320
+ </div>
321
+ <div class="mb-4">
322
+ <p class="text-gray-700">Select a horse to enter in today's race. Winning earns prize money based on the horse's performance.</p>
323
+ <p class="text-sm text-gray-500">Injured horses cannot race. Check training history for injury status.</p>
324
+ </div>
325
+ <div id="racable-horses" class="space-y-3">
326
+ <!-- Racable horses will be added here -->
327
+ </div>
328
+ </div>
329
+ </div>
330
+
331
+ <!-- Notification Modal -->
332
+ <div id="notification-modal" class="game-modal">
333
+ <div class="modal-content w-11/12 sm:w-96 bg-white">
334
+ <div class="flex justify-end mb-2">
335
+ <button id="close-notification-btn" class="text-gray-500 hover:text-gray-700">
336
+ <i class="fas fa-times"></i>
337
+ </button>
338
+ </div>
339
+ <div id="notification-content" class="text-center p-4">
340
+ <!-- Notification content will be added here -->
341
+ </div>
342
+ <button id="notification-ok-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition mt-2">
343
+ OK
344
+ </button>
345
+ </div>
346
+ </div>
347
+
348
+ <script>
349
+ // Game state
350
+ const gameState = {
351
+ day: 1,
352
+ money: 10000,
353
+ stable: [],
354
+ market: [],
355
+ raceInProgress: false,
356
+ raceResults: [],
357
+ notifications: [],
358
+ lastAction: null,
359
+ trainedHorses: new Set(),
360
+ injuredHorses: new Set()
361
+ };
362
+
363
+ // Horse traits
364
+ const horseTraits = [
365
+ { name: "Fast Starter", effect: { speed: 5, endurance: -2 } },
366
+ { name: "Slow but Steady", effect: { speed: -3, endurance: 5 } },
367
+ { name: "High Stamina", effect: { endurance: 6 } },
368
+ { name: "Sprinter", effect: { speed: 8, endurance: -4 } },
369
+ { name: "Fragile", effect: { endurance: -4 } },
370
+ { name: "Strong", effect: { endurance: 3, speed: 2 } },
371
+ { name: "Lazy", effect: { speed: -3, endurance: -2 } },
372
+ { name: "Energetic", effect: { speed: 3, endurance: 3 } },
373
+ { name: "Proud", effect: { beauty: 5 } },
374
+ { name: "Fertile Bloodline", effect: { fertility: 15 } },
375
+ { name: "Unlucky", effect: { speed: -2, endurance: -2 } }
376
+ ];
377
+
378
+ // DOM elements
379
+ const elements = {
380
+ moneyDisplay: document.getElementById('money-display'),
381
+ dayDisplay: document.getElementById('day-display'),
382
+ stableHorses: document.getElementById('stable-horses'),
383
+ stableCount: document.getElementById('stable-count'),
384
+ nextDayBtn: document.getElementById('next-day-btn'),
385
+ marketBtn: document.getElementById('market-btn'),
386
+ breedBtn: document.getElementById('breed-btn'),
387
+ trainBtn: document.getElementById('train-btn'),
388
+ raceBtn: document.getElementById('race-btn'),
389
+ sellBtn: document.getElementById('sell-btn'),
390
+ marketModal: document.getElementById('market-modal'),
391
+ breedModal: document.getElementById('breed-modal'),
392
+ trainModal: document.getElementById('train-modal'),
393
+ sellModal: document.getElementById('sell-modal'),
394
+ raceModal: document.getElementById('race-modal'),
395
+ notificationModal: document.getElementById('notification-modal'),
396
+ marketHorses: document.getElementById('market-horses'),
397
+ sireOptions: document.getElementById('sire-options'),
398
+ damOptions: document.getElementById('dam-options'),
399
+ breedingOptions: document.getElementById('breeding-options'),
400
+ breedingPreview: document.getElementById('breeding-preview'),
401
+ externalBreeding: document.getElementById('external-breeding'),
402
+ externalOptions: document.getElementById('external-options'),
403
+ trainableHorses: document.getElementById('trainable-horses'),
404
+ sellableHorses: document.getElementById('sellable-horses'),
405
+ racableHorses: document.getElementById('racable-horses'),
406
+ notificationContent: document.getElementById('notification-content'),
407
+ raceTrack: document.getElementById('race-track'),
408
+ raceResults: document.getElementById('race-results'),
409
+ raceResultsList: document.getElementById('race-results-list'),
410
+ upcomingRaces: document.getElementById('upcoming-races'),
411
+ gameMessages: document.querySelector('#game-messages div')
412
+ };
413
+
414
+ // Event listeners
415
+ document.getElementById('close-market-btn').addEventListener('click', () => elements.marketModal.style.display = 'none');
416
+ document.getElementById('close-breed-btn').addEventListener('click', () => elements.breedModal.style.display = 'none');
417
+ document.getElementById('close-train-btn').addEventListener('click', () => elements.trainModal.style.display = 'none');
418
+ document.getElementById('close-sell-btn').addEventListener('click', () => elements.sellModal.style.display = 'none');
419
+ document.getElementById('close-race-btn').addEventListener('click', () => elements.raceModal.style.display = 'none');
420
+ document.getElementById('close-notification-btn').addEventListener('click', () => elements.notificationModal.style.display = 'none');
421
+ document.getElementById('notification-ok-btn').addEventListener('click', () => elements.notificationModal.style.display = 'none');
422
+
423
+ elements.nextDayBtn.addEventListener('click', nextDay);
424
+ elements.marketBtn.addEventListener('click', openMarket);
425
+ elements.breedBtn.addEventListener('click', openBreeding);
426
+ elements.trainBtn.addEventListener('click', openTraining);
427
+ elements.sellBtn.addEventListener('click', openSell);
428
+ elements.raceBtn.addEventListener('click', openRace);
429
+
430
+ // Initialize game
431
+ initGame();
432
+
433
+ function initGame() {
434
+ updateUI();
435
+ generateMarketHorses();
436
+ addGameMessage("Welcome to Stable Masters! Buy your first horse to get started.");
437
+ }
438
+
439
+ function updateUI() {
440
+ // Update money and day displays
441
+ elements.moneyDisplay.innerHTML = `<i class="fas fa-coins mr-2"></i> $${gameState.money.toLocaleString()}`;
442
+ elements.dayDisplay.innerHTML = `<i class="far fa-calendar-alt mr-2"></i> Day ${gameState.day}`;
443
+
444
+ // Update stable count
445
+ const horseCount = gameState.stable.length;
446
+ elements.stableCount.textContent = `${horseCount} ${horseCount === 1 ? 'horse' : 'horses'}`;
447
+
448
+ // Enable/disable action buttons based on game state
449
+ elements.breedBtn.disabled = !canBreed();
450
+ if (!elements.breedBtn.disabled) {
451
+ elements.breedBtn.classList.remove('opacity-50', 'cursor-not-allowed');
452
+ } else {
453
+ elements.breedBtn.classList.add('opacity-50', 'cursor-not-allowed');
454
+ }
455
+
456
+ elements.trainBtn.disabled = gameState.stable.length === 0;
457
+ if (!elements.trainBtn.disabled) {
458
+ elements.trainBtn.classList.remove('opacity-50', 'cursor-not-allowed');
459
+ } else {
460
+ elements.trainBtn.classList.add('opacity-50', 'cursor-not-allowed');
461
+ }
462
+
463
+ elements.raceBtn.disabled = gameState.stable.length === 0;
464
+ if (!elements.raceBtn.disabled) {
465
+ elements.raceBtn.classList.remove('opacity-50', 'cursor-not-allowed');
466
+ } else {
467
+ elements.raceBtn.classList.add('opacity-50', 'cursor-not-allowed');
468
+ }
469
+
470
+ elements.sellBtn.disabled = gameState.stable.length === 0;
471
+ if (!elements.sellBtn.disabled) {
472
+ elements.sellBtn.classList.remove('opacity-50', 'cursor-not-allowed');
473
+ } else {
474
+ elements.sellBtn.classList.add('opacity-50', 'cursor-not-allowed');
475
+ }
476
+
477
+ // Update upcoming races display
478
+ updateUpcomingRaces();
479
+ }
480
+
481
+ function updateStableDisplay() {
482
+ elements.stableHorses.innerHTML = '';
483
+
484
+ if (gameState.stable.length === 0) {
485
+ elements.stableHorses.innerHTML = '<p class="text-gray-500 italic">No horses in your stable yet. Visit the market to buy your first horse!</p>';
486
+ return;
487
+ }
488
+
489
+ gameState.stable.forEach((horse, index) => {
490
+ const horseCard = document.createElement('div');
491
+ horseCard.className = 'bg-gray-50 rounded-lg p-3 border border-gray-200';
492
+
493
+ const genderClass = horse.gender === 'male' ? 'horse-male' : 'horse-female';
494
+ const genderIcon = horse.gender === 'male' ? 'mars' : 'venus';
495
+ const injuredClass = gameState.injuredHorses.has(horse.id) ? 'bg-red-100 border-red-300' : '';
496
+
497
+ horseCard.className += ` ${injuredClass}`;
498
+
499
+ const trainedTimes = horse.trainingCount || 0;
500
+ const trainedIndicator = trainedTimes > 0 ? `<span class="text-xs bg-yellow-100 text-yellow-800 px-2 py-1 rounded">Trained ${trainedTimes}/3</span>` : '';
501
+ const injuredIndicator = gameState.injuredHorses.has(horse.id) ? `<span class="text-xs bg-red-100 text-red-800 px-2 py-1 rounded">Injured</span>` : '';
502
+
503
+ horseCard.innerHTML = `
504
+ <div class="flex justify-between items-start mb-2">
505
+ <h3 class="font-bold text-lg ${genderClass}">
506
+ <i class="fas fa-${genderIcon} mr-2"></i>${horse.name}
507
+ </h3>
508
+ <div class="flex space-x-2">
509
+ ${trainedIndicator}
510
+ ${injuredIndicator}
511
+ </div>
512
+ </div>
513
+ <div class="grid grid-cols-2 gap-2 mb-3">
514
+ <div>
515
+ <div class="text-xs text-gray-500 mb-1">Speed</div>
516
+ <div class="flex items-center">
517
+ <div class="stats-bar stat-speed" style="width: ${horse.speed * 7}px;"></div>
518
+ <span class="text-xs ml-2 font-semibold">${horse.speed}</span>
519
+ </div>
520
+ </div>
521
+ <div>
522
+ <div class="text-xs text-gray-500 mb-1">Endurance</div>
523
+ <div class="flex items-center">
524
+ <div class="stats-bar stat-endurance" style="width: ${horse.endurance * 7}px;"></div>
525
+ <span class="text-xs ml-2 font-semibold">${horse.endurance}</span>
526
+ </div>
527
+ </div>
528
+ <div>
529
+ <div class="text-xs text-gray-500 mb-1">Beauty</div>
530
+ <div class="flex items-center">
531
+ <div class="stats-bar stat-beauty" style="width: ${horse.beauty * 7}px;"></div>
532
+ <span class="text-xs ml-2 font-semibold">${horse.beauty}</span>
533
+ </div>
534
+ </div>
535
+ <div>
536
+ <div class="text-xs text-gray-500 mb-1">Fertility</div>
537
+ <div class="flex items-center">
538
+ <div class="stats-bar stat-fertility" style="width: ${horse.fertility * 1.5}px;"></div>
539
+ <span class="text-xs ml-2 font-semibold">${horse.fertility}%</span>
540
+ </div>
541
+ </div>
542
+ </div>
543
+ ${horse.traits.length > 0 ?
544
+ `<div class="text-xs mb-2">
545
+ <span class="font-semibold">Traits:</span> ${horse.traits.map(t => t.name).join(', ')}
546
+ </div>` : ''
547
+ }
548
+ <div class="text-xs text-gray-500">
549
+ Value: $${calculateHorseValue(horse).toLocaleString()}
550
+ </div>
551
+ `;
552
+
553
+ elements.stableHorses.appendChild(horseCard);
554
+ });
555
+ }
556
+
557
+ function generateMarketHorses() {
558
+ gameState.market = [];
559
+
560
+ // Determine how many horses to generate (3, but ensure at least one is affordable)
561
+ const count = 3;
562
+ let hasAffordable = false;
563
+
564
+ for (let i = 0; i < count; i++) {
565
+ const affordable = !hasAffordable || Math.random() > 0.5;
566
+ const horse = generateHorse(affordable ? gameState.money * 0.8 : gameState.money * 2);
567
+ gameState.market.push(horse);
568
+
569
+ if (horse.price <= gameState.money) {
570
+ hasAffordable = true;
571
+ }
572
+ }
573
+
574
+ // If we didn't get any affordable horses, replace the last one with an affordable one
575
+ if (!hasAffordable) {
576
+ const affordableHorse = generateHorse(gameState.money * 0.8);
577
+ gameState.market[count - 1] = affordableHorse;
578
+ }
579
+ }
580
+
581
+ function generateHorse(targetPrice) {
582
+ const gender = Math.random() > 0.5 ? 'male' : 'female';
583
+ const name = generateHorseName(gender);
584
+
585
+ // Base stats (5-15)
586
+ const speed = 5 + Math.floor(Math.random() * 11);
587
+ const endurance = 5 + Math.floor(Math.random() * 11);
588
+ const beauty = 5 + Math.floor(Math.random() * 11);
589
+ const fertility = 80 + Math.floor(Math.random() * 21); // 80-100%
590
+
591
+ // Generate 0-2 random traits
592
+ const traits = [];
593
+ const traitCount = Math.floor(Math.random() * 3);
594
+
595
+ for (let i = 0; i < traitCount; i++) {
596
+ const traitIndex = Math.floor(Math.random() * horseTraits.length);
597
+ traits.push({...horseTraits[traitIndex]});
598
+ }
599
+
600
+ // Apply trait effects
601
+ let effectiveSpeed = speed;
602
+ let effectiveEndurance = endurance;
603
+ let effectiveBeauty = beauty;
604
+ let effectiveFertility = fertility;
605
+
606
+ traits.forEach(trait => {
607
+ effectiveSpeed += trait.effect.speed || 0;
608
+ effectiveEndurance += trait.effect.endurance || 0;
609
+ effectiveBeauty += trait.effect.beauty || 0;
610
+ effectiveFertility += trait.effect.fertility || 0;
611
+ });
612
+
613
+ // Clamp values
614
+ effectiveSpeed = Math.max(1, Math.min(20, effectiveSpeed));
615
+ effectiveEndurance = Math.max(1, Math.min(20, effectiveEndurance));
616
+ effectiveBeauty = Math.max(1, Math.min(20, effectiveBeauty));
617
+ effectiveFertility = Math.max(0, Math.min(100, effectiveFertility));
618
+
619
+ // Calculate price based on stats
620
+ let price = (effectiveSpeed * 500) + (effectiveEndurance * 400) + (effectiveBeauty * 300);
621
+
622
+ // Adjust price for traits and fertility
623
+ price *= 1 + (traits.length * 0.15);
624
+ price *= 0.8 + (effectiveFertility / 100 * 0.4);
625
+
626
+ // If target price is specified, adjust towards it while maintaining relative stat values
627
+ if (targetPrice) {
628
+ const adjustmentFactor = targetPrice / price * (0.8 + Math.random() * 0.4);
629
+ price = Math.floor(price * adjustmentFactor);
630
+ }
631
+
632
+ // Add some randomness to final price
633
+ price = Math.floor(price * (0.9 + Math.random() * 0.2));
634
+
635
+ return {
636
+ id: Date.now() + Math.floor(Math.random() * 1000),
637
+ name,
638
+ gender,
639
+ speed,
640
+ endurance,
641
+ beauty,
642
+ fertility,
643
+ traits,
644
+ price,
645
+ effectiveSpeed,
646
+ effectiveEndurance,
647
+ effectiveBeauty,
648
+ effectiveFertility,
649
+ parents: [],
650
+ trainingCount: 0
651
+ };
652
+ }
653
+
654
+ function generateHorseName(gender) {
655
+ const prefixes = ['Thunder', 'Silver', 'Golden', 'Midnight', 'Royal', 'Wild', 'Swift', 'Black', 'White', 'Red'];
656
+ const suffixes = ['Star', 'Shadow', 'Fire', 'Dream', 'Spirit', 'Wind', 'Storm', 'Mystery', 'Blaze', 'Dancer'];
657
+ const maleSpecific = ['King', 'Prince', 'Duke', 'Sir', 'Knight'];
658
+ const femaleSpecific = ['Queen', 'Princess', 'Lady', 'Grace', 'Rose'];
659
+
660
+ let name;
661
+
662
+ if (gender === 'male' && Math.random() > 0.5) {
663
+ name = maleSpecific[Math.floor(Math.random() * maleSpecific.length)];
664
+ } else if (gender === 'female' && Math.random() > 0.5) {
665
+ name = femaleSpecific[Math.floor(Math.random() * femaleSpecific.length)];
666
+ } else {
667
+ name = prefixes[Math.floor(Math.random() * prefixes.length)] + ' ' + suffixes[Math.floor(Math.random() * suffixes.length)];
668
+ }
669
+
670
+ return name;
671
+ }
672
+
673
+ function updateMarketDisplay() {
674
+ elements.marketHorses.innerHTML = '';
675
+
676
+ if (gameState.market.length === 0) {
677
+ elements.noHorsesMsg.classList.remove('hidden');
678
+ return;
679
+ }
680
+
681
+ gameState.market.forEach(horse => {
682
+ const horseCard = document.createElement('div');
683
+ horseCard.className = 'bg-gray-50 rounded-lg p-4 border border-gray-300';
684
+
685
+ const genderClass = horse.gender === 'male' ? 'horse-male' : 'horse-female';
686
+ const genderIcon = horse.gender === 'male' ? 'mars' : 'venus';
687
+ const canAfford = gameState.money >= horse.price;
688
+ const buttonClass = canAfford ? 'bg-green-600 hover:bg-green-700' : 'bg-gray-400 cursor-not-allowed';
689
+
690
+ horseCard.innerHTML = `
691
+ <div class="flex justify-between items-start mb-3">
692
+ <h3 class="font-bold text-lg ${genderClass}">
693
+ <i class="fas fa-${genderIcon} mr-2"></i>${horse.name}
694
+ </h3>
695
+ <span class="font-bold text-yellow-600">$${horse.price.toLocaleString()}</span>
696
+ </div>
697
+ <div class="grid grid-cols-2 gap-2 mb-3">
698
+ <div>
699
+ <div class="text-xs text-gray-500 mb-1">Speed</div>
700
+ <div class="flex items-center">
701
+ <div class="stats-bar stat-speed" style="width: ${horse.effectiveSpeed * 7}px;"></div>
702
+ <span class="text-xs ml-2 font-semibold">${horse.effectiveSpeed}</span>
703
+ </div>
704
+ </div>
705
+ <div>
706
+ <div class="text-xs text-gray-500 mb-1">Endurance</div>
707
+ <div class="flex items-center">
708
+ <div class="stats-bar stat-endurance" style="width: ${horse.effectiveEndurance * 7}px;"></div>
709
+ <span class="text-xs ml-2 font-semibold">${horse.effectiveEndurance}</span>
710
+ </div>
711
+ </div>
712
+ <div>
713
+ <div class="text-xs text-gray-500 mb-1">Beauty</div>
714
+ <div class="flex items-center">
715
+ <div class="stats-bar stat-beauty" style="width: ${horse.effectiveBeauty * 7}px;"></div>
716
+ <span class="text-xs ml-2 font-semibold">${horse.effectiveBeauty}</span>
717
+ </div>
718
+ </div>
719
+ <div>
720
+ <div class="text-xs text-gray-500 mb-1">Fertility</div>
721
+ <div class="flex items-center">
722
+ <div class="stats-bar stat-fertility" style="width: ${horse.effectiveFertility * 1.5}px;"></div>
723
+ <span class="text-xs ml-2 font-semibold">${horse.effectiveFertility}%</span>
724
+ </div>
725
+ </div>
726
+ </div>
727
+ ${horse.traits.length > 0 ?
728
+ `<div class="text-xs mb-3">
729
+ <span class="font-semibold">Traits:</span> ${horse.traits.map(t => t.name).join(', ')}
730
+ </div>` : ''
731
+ }
732
+ <button class="w-full ${buttonClass} text-white font-bold py-2 px-4 rounded transition buy-horse-btn"
733
+ data-horse-id="${horse.id}" ${canAfford ? '' : 'disabled'}>
734
+ <i class="fas fa-shopping-cart mr-2"></i>Buy Horse
735
+ </button>
736
+ `;
737
+
738
+ elements.marketHorses.appendChild(horseCard);
739
+ });
740
+
741
+ // Add event listeners to buy buttons
742
+ document.querySelectorAll('.buy-horse-btn').forEach(btn => {
743
+ btn.addEventListener('click', function() {
744
+ const horseId = parseInt(this.getAttribute('data-horse-id'));
745
+ const horse = gameState.market.find(h => h.id === horseId);
746
+
747
+ if (horse && gameState.money >= horse.price) {
748
+ buyHorse(horse);
749
+ }
750
+ });
751
+ });
752
+ }
753
+
754
+ function buyHorse(horse) {
755
+ gameState.money -= horse.price;
756
+ gameState.stable.push(horse);
757
+ gameState.market = gameState.market.filter(h => h.id !== horse.id);
758
+
759
+ addGameMessage(`You purchased ${horse.name} for $${horse.price.toLocaleString()}.`);
760
+
761
+ // Enable breeding if possible
762
+ if (canBreed()) {
763
+ elements.breedBtn.disabled = false;
764
+ elements.breedBtn.classList.remove('opacity-50', 'cursor-not-allowed');
765
+ }
766
+
767
+ updateUI();
768
+ updateStableDisplay();
769
+ elements.marketModal.style.display = 'none';
770
+ }
771
+
772
+ function canBreed() {
773
+ if (gameState.stable.length < 2) return false;
774
+
775
+ const hasMale = gameState.stable.some(h => h.gender === 'male' && h.effectiveFertility > 0);
776
+ const hasFemale = gameState.stable.some(h => h.gender === 'female' && h.effectiveFertility > 0);
777
+
778
+ return hasMale && hasFemale;
779
+ }
780
+
781
+ function openMarket() {
782
+ if (gameState.market.length === 0) {
783
+ generateMarketHorses();
784
+ }
785
+
786
+ updateMarketDisplay();
787
+ elements.marketModal.style.display = 'flex';
788
+ }
789
+
790
+ function openBreeding() {
791
+ if (!canBreed()) {
792
+ showNotification("Breeding Error", "You need at least one male and one female horse with fertility remaining to breed.");
793
+ return;
794
+ }
795
+
796
+ // Separate male and female horses with fertility
797
+ const sires = gameState.stable.filter(h => h.gender === 'male' && h.effectiveFertility > 0);
798
+ const dams = gameState.stable.filter(h => h.gender === 'female' && h.effectiveFertility > 0);
799
+
800
+ // Populate sire options
801
+ elements.sireOptions.innerHTML = '';
802
+ sires.forEach(sire => {
803
+ const sireCard = createBreedingOptionCard(sire, 'sire');
804
+ elements.sireOptions.appendChild(sireCard);
805
+ });
806
+
807
+ // Populate dam options
808
+ elements.damOptions.innerHTML = '';
809
+ dams.forEach(dam => {
810
+ const damCard = createBreedingOptionCard(dam, 'dam');
811
+ elements.damOptions.appendChild(damCard);
812
+ });
813
+
814
+ // Hide breeding preview initially
815
+ elements.breedingOptions.classList.add('hidden');
816
+ elements.externalBreeding.classList.add('hidden');
817
+
818
+ // Show breeding modal
819
+ elements.breedModal.style.display = 'flex';
820
+ }
821
+
822
+ function createBreedingOptionCard(horse, role) {
823
+ const card = document.createElement('div');
824
+ card.className = 'bg-gray-50 rounded-lg p-3 border border-gray-300 flex items-center justify-between';
825
+ card.dataset.horseId = horse.id;
826
+ card.dataset.role = role;
827
+
828
+ const genderClass = horse.gender === 'male' ? 'horse-male' : 'horse-female';
829
+ const genderIcon = horse.gender === 'male' ? 'mars' : 'venus';
830
+
831
+ card.innerHTML = `
832
+ <div class="flex items-center">
833
+ <div class="mr-3 ${genderClass} font-bold">
834
+ <i class="fas fa-${genderIcon}"></i>
835
+ </div>
836
+ <div>
837
+ <div class="font-semibold">${horse.name}</div>
838
+ <div class="text-xs">
839
+ <span class="font-semibold">Fertility:</span> ${horse.effectiveFertility}%
840
+ </div>
841
+ </div>
842
+ </div>
843
+ <div class="text-right">
844
+ <div class="font-semibold text-sm">$${calculateBreedingValue(horse).toLocaleString()}</div>
845
+ <div class="text-xs text-gray-500">breed value</div>
846
+ </div>
847
+ `;
848
+
849
+ card.addEventListener('click', function() {
850
+ selectBreedingHorse(horse, role);
851
+ });
852
+
853
+ return card;
854
+ }
855
+
856
+ function selectBreedingHorse(horse, role) {
857
+ // Remove highlight from all options
858
+ document.querySelectorAll(`#${role}-options > div`).forEach(opt => {
859
+ opt.classList.remove('border-blue-500', 'border-2', 'bg-blue-50');
860
+ });
861
+
862
+ // Highlight selected option
863
+ document.querySelector(`#${role}-options > div[data-horse-id="${horse.id}"]`)
864
+ .classList.add('border-blue-500', 'border-2', 'bg-blue-50');
865
+
866
+ // Check if we have both sire and dam selected to show breeding preview
867
+ const selectedSire = document.querySelector('#sire-options > div.border-blue-500');
868
+ const selectedDam = document.querySelector('#dam-options > div.border-blue-500');
869
+
870
+ if (selectedSire && selectedDam) {
871
+ showBreedingPreview(selectedSire.dataset.horseId, selectedDam.dataset.horseId);
872
+ }
873
+ }
874
+
875
+ function showBreedingPreview(sireId, damId) {
876
+ const sire = gameState.stable.find(h => h.id === parseInt(sireId));
877
+ const dam = gameState.stable.find(h => h.id === parseInt(damId));
878
+
879
+ if (!sire || !dam) return;
880
+
881
+ // Calculate potential offspring stats (average with randomness)
882
+ const speed = Math.floor((sire.speed + dam.speed) / 2 + (Math.random() * 4 - 2));
883
+ const endurance = Math.floor((sire.endurance + dam.endurance) / 2 + (Math.random() * 4 - 2));
884
+ const beauty = Math.floor((sire.beauty + dam.beauty) / 2 + (Math.random() * 4 - 2));
885
+ const fertility = 70 + Math.floor(Math.random() * 31); // 70-100%
886
+
887
+ // Determine traits (inherit from parents with chance)
888
+ const traits = [];
889
+
890
+ // Combine all parent traits and randomly inherit some
891
+ const allTraits = [...sire.traits, ...dam.traits];
892
+ allTraits.forEach(trait => {
893
+ if (Math.random() * 100 < (sire.effectiveFertility + dam.effectiveFertility) / 4) {
894
+ traits.push({...trait});
895
+ }
896
+ });
897
+
898
+ // Small chance for new random trait
899
+ if (Math.random() < 0.1) {
900
+ const randomTraitIndex = Math.floor(Math.random() * horseTraits.length);
901
+ traits.push({...horseTraits[randomTraitIndex]});
902
+ }
903
+
904
+ // Apply trait effects to preview stats (just for display)
905
+ let previewSpeed = speed;
906
+ let previewEndurance = endurance;
907
+ let previewBeauty = beauty;
908
+ let previewFertility = fertility;
909
+
910
+ traits.forEach(trait => {
911
+ previewSpeed += trait.effect.speed || 0;
912
+ previewEndurance += trait.effect.endurance || 0;
913
+ previewBeauty += trait.effect.beauty || 0;
914
+ previewFertility += trait.effect.fertility || 0;
915
+ });
916
+
917
+ // Clamp preview values
918
+ previewSpeed = Math.max(1, Math.min(20, previewSpeed));
919
+ previewEndurance = Math.max(1, Math.min(20, previewEndurance));
920
+ previewBeauty = Math.max(1, Math.min(20, previewBeauty));
921
+ previewFertility = Math.max(0, Math.min(100, previewFertility));
922
+
923
+ // Determine gender
924
+ const gender = Math.random() > 0.5 ? 'male' : 'female';
925
+ const genderClass = gender === 'male' ? 'horse-male' : 'horse-female';
926
+ const genderIcon = gender === 'male' ? 'mars' : 'venus';
927
+
928
+ // Update breeding preview
929
+ elements.breedingPreview.innerHTML = `
930
+ <div>
931
+ <h4 class="font-bold">${sire.name}</h4>
932
+ <div class="text-xs mb-2">
933
+ <span class="font-semibold">Speed:</span> ${sire.speed},
934
+ <span class="font-semibold">Endurance:</span> ${sire.endurance}
935
+ </div>
936
+ <div class="text-xs">
937
+ <span class="font-semibold">Traits:</span> ${sire.traits.length > 0 ? sire.traits.map(t => t.name).join(', ') : 'None'}
938
+ </div>
939
+ </div>
940
+ <div class="text-center">
941
+ <div class="text-4xl mb-2">+</div>
942
+ <div class="text-sm">Fertility: Sire (${sire.effectiveFertility}%) + Dam (${dam.effectiveFertility}%)</div>
943
+ </div>
944
+ <div>
945
+ <h4 class="font-bold">${dam.name}</h4>
946
+ <div class="text-xs mb-2">
947
+ <span class="font-semibold">Speed:</span> ${dam.speed},
948
+ <span class="font-semibold">Endurance:</span> ${dam.endurance}
949
+ </div>
950
+ <div class="text-xs">
951
+ <span class="font-semibold">Traits:</span> ${dam.traits.length > 0 ? dam.traits.map(t => t.name).join(', ') : 'None'}
952
+ </div>
953
+ </div>
954
+ <div class="col-span-3">
955
+ <div class="border-t-2 my-2 border-purple-200"></div>
956
+ <h4 class="font-bold ${genderClass} mb-2">
957
+ <i class="fas fa-${genderIcon} mr-2"></i>Potential Offspring
958
+ </h4>
959
+ <div class="grid grid-cols-3 gap-2 mb-2">
960
+ <div>
961
+ <div class="text-xs text-gray-500 mb-1">Speed</div>
962
+ <div class="flex items-center">
963
+ <div class="stats-bar stat-speed" style="width: ${previewSpeed * 6}px;"></div>
964
+ <span class="text-xs ml-2 font-semibold">${previewSpeed}</span>
965
+ </div>
966
+ </div>
967
+ <div>
968
+ <div class="text-xs text-gray-500 mb-1">Endurance</div>
969
+ <div class="flex items-center">
970
+ <div class="stats-bar stat-endurance" style="width: ${previewEndurance * 6}px;"></div>
971
+ <span class="text-xs ml-2 font-semibold">${previewEndurance}</span>
972
+ </div>
973
+ </div>
974
+ <div>
975
+ <div class="text-xs text-gray-500 mb-1">Fertility</div>
976
+ <div class="flex items-center">
977
+ <div class="stats-bar stat-fertility" style="width: ${previewFertility * 1.5}px;"></div>
978
+ <span class="text-xs ml-2 font-semibold">${previewFertility}%</span>
979
+ </div>
980
+ </div>
981
+ </div>
982
+ <div class="text-xs">
983
+ <span class="font-semibold">Possible Traits:</span> ${traits.length > 0 ? traits.map(t => t.name).join(', ') : 'None'}
984
+ </div>
985
+ </div>
986
+ `;
987
+
988
+ // Show breeding options
989
+ elements.breedingOptions.classList.remove('hidden');
990
+
991
+ // Set up confirm breeding button
992
+ elements.confirmBreedBtn.onclick = () => {
993
+ breedHorses(sire, dam);
994
+ };
995
+ }
996
+
997
+ function breedHorses(sire, dam) {
998
+ // Genetic mixing
999
+ const speed = Math.floor((sire.speed + dam.speed) / 2 + (Math.random() * 4 - 2));
1000
+ const endurance = Math.floor((sire.endurance + dam.endurance) / 2 + (Math.random() * 4 - 2));
1001
+ const beauty = Math.floor((sire.beauty + dam.beauty) / 2 + (Math.random() * 4 - 2));
1002
+ const fertility = 70 + Math.floor(Math.random() * 31); // 70-100%
1003
+ const gender = Math.random() > 0.5 ? 'male' : 'female';
1004
+
1005
+ // Determine name (combine parts of parent names)
1006
+ const sireNameParts = sire.name.split(' ');
1007
+ const damNameParts = dam.name.split(' ');
1008
+ const prefix = sireNameParts.length > 1 ? sireNameParts[0] : damNameParts[0];
1009
+ const suffix = damNameParts.length > 1 ? damNameParts[1] : sireNameParts[0];
1010
+ const name = `${prefix} ${suffix}`;
1011
+
1012
+ // Inherit traits
1013
+ const traits = [];
1014
+
1015
+ // Combine all parent traits and randomly inherit some
1016
+ const allTraits = [...sire.traits, ...dam.traits];
1017
+ allTraits.forEach(trait => {
1018
+ if (Math.random() * 100 < (sire.effectiveFertility + dam.effectiveFertility) / 4) {
1019
+ traits.push({...trait});
1020
+ }
1021
+ });
1022
+
1023
+ // Small chance for new random trait
1024
+ if (Math.random() < 0.1) {
1025
+ const randomTraitIndex = Math.floor(Math.random() * horseTraits.length);
1026
+ traits.push({...horseTraits[randomTraitIndex]});
1027
+ }
1028
+
1029
+ // Apply trait effects
1030
+ let effectiveSpeed = speed;
1031
+ let effectiveEndurance = endurance;
1032
+ let effectiveBeauty = beauty;
1033
+ let effectiveFertility = fertility;
1034
+
1035
+ traits.forEach(trait => {
1036
+ effectiveSpeed += trait.effect.speed || 0;
1037
+ effectiveEndurance += trait.effect.endurance || 0;
1038
+ effectiveBeauty += trait.effect.beauty || 0;
1039
+ effectiveFertility += trait.effect.fertility || 0;
1040
+ });
1041
+
1042
+ // Clamp values
1043
+ effectiveSpeed = Math.max(1, Math.min(20, effectiveSpeed));
1044
+ effectiveEndurance = Math.max(1, Math.min(20, effectiveEndurance));
1045
+ effectiveBeauty = Math.max(1, Math.min(20, effectiveBeauty));
1046
+ effectiveFertility = Math.max(0, Math.min(100, effectiveFertility));
1047
+
1048
+ // Create offspring
1049
+ const offspring = {
1050
+ id: Date.now() + Math.floor(Math.random() * 1000),
1051
+ name,
1052
+ gender,
1053
+ speed,
1054
+ endurance,
1055
+ beauty,
1056
+ fertility,
1057
+ effectiveSpeed,
1058
+ effectiveEndurance,
1059
+ effectiveBeauty,
1060
+ effectiveFertility,
1061
+ traits,
1062
+ parents: [sire.id, dam.id],
1063
+ trainingCount: 0
1064
+ };
1065
+
1066
+ // Add to stable
1067
+ gameState.stable.push(offspring);
1068
+
1069
+ // Decrease parent fertility
1070
+ sire.fertility = Math.max(0, sire.fertility - 25);
1071
+ sire.effectiveFertility = Math.max(0, Math.min(100, sire.fertility + (sire.traits.reduce((sum, t) => sum + (t.effect.fertility || 0), 0))));
1072
+
1073
+ dam.fertility = Math.max(0, dam.fertility - 25);
1074
+ dam.effectiveFertility = Math.max(0, Math.min(100, dam.fertility + (dam.traits.reduce((sum, t) => sum + (t.effect.fertility || 0), 0))));
1075
+
1076
+ // Update UI
1077
+ updateUI();
1078
+ updateStableDisplay();
1079
+ elements.breedModal.style.display = 'none';
1080
+
1081
+ addGameMessage(`You bred ${sire.name} and ${dam.name}. Their offspring ${name} has been added to your stable!`);
1082
+
1083
+ // Show birth notification
1084
+ showBirthNotification(offspring);
1085
+ }
1086
+
1087
+ function showBirthNotification(horse) {
1088
+ const genderClass = horse.gender === 'male' ? 'horse-male' : 'horse-female';
1089
+ const genderIcon = horse.gender === 'male' ? 'mars' : 'venus';
1090
+
1091
+ showNotification("New Foal Born!", `
1092
+ <div class="text-center mb-4">
1093
+ <div class="${genderClass} text-4xl mb-2 inline-block rounded-full p-3">
1094
+ <i class="fas fa-${genderIcon}"></i>
1095
+ </div>
1096
+ <h3 class="text-xl font-bold">${horse.name}</h3>
1097
+ <div class="text-sm">${horse.gender === 'male' ? 'Colt' : 'Filly'}</div>
1098
+ </div>
1099
+ <div class="grid grid-cols-2 gap-3 text-sm">
1100
+ <div class="text-center">
1101
+ <div class="text-xs text-gray-500 mb-1">Speed</div>
1102
+ <div class="font-semibold">${horse.effectiveSpeed}</div>
1103
+ </div>
1104
+ <div class="text-center">
1105
+ <div class="text-xs text-gray-500 mb-1">Endurance</div>
1106
+ <div class="font-semibold">${horse.effectiveEndurance}</div>
1107
+ </div>
1108
+ <div class="text-center">
1109
+ <div class="text-xs text-gray-500 mb-1">Beauty</div>
1110
+ <div class="font-semibold">${horse.effectiveBeauty}</div>
1111
+ </div>
1112
+ <div class="text-center">
1113
+ <div class="text-xs text-gray-500 mb-1">Fertility</div>
1114
+ <div class="font-semibold">${horse.effectiveFertility}%</div>
1115
+ </div>
1116
+ </div>
1117
+ ${horse.traits.length > 0 ?
1118
+ `<div class="mt-4 text-sm">
1119
+ <span class="font-semibold">Traits:</span> ${horse.traits.map(t => t.name).join(', ')}
1120
+ </div>` : ''
1121
+ }
1122
+ `);
1123
+ }
1124
+
1125
+ function openTraining() {
1126
+ elements.trainableHorses.innerHTML = '';
1127
+
1128
+ gameState.stable.forEach(horse => {
1129
+ // Skip injured horses
1130
+ if (gameState.injuredHorses.has(horse.id)) return;
1131
+
1132
+ // Skip horses that have been trained 3 times already
1133
+ if (horse.trainingCount >= 3) return;
1134
+
1135
+ const horseCard = document.createElement('div');
1136
+ horseCard.className = 'bg-gray-50 rounded-lg p-3 border border-gray-300';
1137
+
1138
+ const genderClass = horse.gender === 'male' ? 'horse-male' : 'horse-female';
1139
+ const genderIcon = horse.gender === 'male' ? 'mars' : 'venus';
1140
+ const trainedTimes = horse.trainingCount || 0;
1141
+ const trainedText = trainedTimes > 0 ? ` (Trained ${trainedTimes}/3 times)` : '';
1142
+
1143
+ horseCard.innerHTML = `
1144
+ <div class="flex justify-between items-center mb-2">
1145
+ <h3 class="font-semibold ${genderClass}">
1146
+ <i class="fas fa-${genderIcon} mr-2"></i>${horse.name}${trainedText}
1147
+ </h3>
1148
+ <button class="bg-yellow-600 hover:bg-yellow-700 text-white text-sm font-bold py-1 px-3 rounded transition train-horse-btn" data-horse-id="${horse.id}">
1149
+ Train
1150
+ </button>
1151
+ </div>
1152
+ <div class="grid grid-cols-2 gap-2">
1153
+ <div>
1154
+ <div class="text-xs text-gray-500 mb-1">Speed</div>
1155
+ <div class="flex items-center">
1156
+ <div class="stats-bar stat-speed" style="width: ${horse.effectiveSpeed * 6}px;"></div>
1157
+ <span class="text-xs ml-2 font-semibold">${horse.effectiveSpeed}</span>
1158
+ </div>
1159
+ </div>
1160
+ <div>
1161
+ <div class="text-xs text-gray-500 mb-1">Endurance</div>
1162
+ <div class="flex items-center">
1163
+ <div class="stats-bar stat-endurance" style="width: ${horse.effectiveEndurance * 6}px;"></div>
1164
+ <span class="text-xs ml-2 font-semibold">${horse.effectiveEndurance}</span>
1165
+ </div>
1166
+ </div>
1167
+ </div>
1168
+ `;
1169
+
1170
+ horseCard.querySelector('.train-horse-btn').addEventListener('click', () => trainHorse(horse));
1171
+
1172
+ elements.trainableHorses.appendChild(horseCard);
1173
+ });
1174
+
1175
+ if (elements.trainableHorses.children.length === 0) {
1176
+ elements.trainableHorses.innerHTML = '<p class="text-gray-500 italic">No horses available for training. All horses have been trained 3 times or are injured.</p>';
1177
+ }
1178
+
1179
+ elements.trainModal.style.display = 'flex';
1180
+ }
1181
+
1182
+ function trainHorse(horse) {
1183
+ // 20% chance of injury
1184
+ const injured = Math.random() < 0.2;
1185
+
1186
+ // Stat improvements (1-3 points randomly assigned)
1187
+ const statPool = ['speed', 'endurance'];
1188
+ const statsToImprove = Math.floor(Math.random() * statPool.length) + 1;
1189
+ const improvedStats = {};
1190
+
1191
+ for (let i = 0; i < statsToImprove; i++) {
1192
+ const stat = statPool[Math.floor(Math.random() * statPool.length)];
1193
+ improvedStats[stat] = (improvedStats[stat] || 0) + 1;
1194
+ }
1195
+
1196
+ // Apply improvements
1197
+ let message = `Training ${horse.name}: `;
1198
+ const improvements = [];
1199
+
1200
+ if (improvedStats.speed) {
1201
+ horse.speed = Math.min(20, horse.speed + improvedStats.speed);
1202
+ horse.effectiveSpeed = horse.speed;
1203
+ // Apply trait effects again
1204
+ horse.traits.forEach(trait => {
1205
+ horse.effectiveSpeed += trait.effect.speed || 0;
1206
+ });
1207
+ horse.effectiveSpeed = Math.max(1, Math.min(20, horse.effectiveSpeed));
1208
+ improvements.push(`Speed +${improvedStats.speed}`);
1209
+ }
1210
+
1211
+ if (improvedStats.endurance) {
1212
+ horse.endurance = Math.min(20, horse.endurance + improvedStats.endurance);
1213
+ horse.effectiveEndurance = horse.endurance;
1214
+ // Apply trait effects again
1215
+ horse.traits.forEach(trait => {
1216
+ horse.effectiveEndurance += trait.effect.endurance || 0;
1217
+ });
1218
+ horse.effectiveEndurance = Math.max(1, Math.min(20, horse.effectiveEndurance));
1219
+ improvements.push(`Endurance +${improvedStats.endurance}`);
1220
+ }
1221
+
1222
+ if (improvements.length > 0) {
1223
+ message += `Improved ${improvements.join(' and ')}. `;
1224
+ } else {
1225
+ message += `No stat improvements this time. `;
1226
+ }
1227
+
1228
+ // Apply injury if it happened
1229
+ if (injured) {
1230
+ gameState.injuredHorses.add(horse.id);
1231
+ message += `Unfortunately, ${horse.name} was injured and can't race until healed!`;
1232
+ } else {
1233
+ message += "No injuries occurred.";
1234
+ }
1235
+
1236
+ // Increment training count
1237
+ horse.trainingCount = (horse.trainingCount || 0) + 1;
1238
+
1239
+ // Update UI
1240
+ updateStableDisplay();
1241
+ addGameMessage(message);
1242
+
1243
+ // Close training modal and reopen to refresh list
1244
+ elements.trainModal.style.display = 'none';
1245
+ openTraining();
1246
+ }
1247
+
1248
+ function openRace() {
1249
+ elements.racableHorses.innerHTML = '';
1250
+
1251
+ gameState.stable.forEach(horse => {
1252
+ // Skip injured horses
1253
+ if (gameState.injuredHorses.has(horse.id)) return;
1254
+
1255
+ const horseCard = document.createElement('div');
1256
+ horseCard.className = 'bg-gray-50 rounded-lg p-3 border border-gray-300';
1257
+
1258
+ const genderClass = horse.gender === 'male' ? 'horse-male' : 'horse-female';
1259
+ const genderIcon = horse.gender === 'male' ? 'mars' : 'venus';
1260
+ const trainedTimes = horse.trainingCount || 0;
1261
+
1262
+ horseCard.innerHTML = `
1263
+ <div class="flex justify-between items-center mb-2">
1264
+ <h3 class="font-semibold ${genderClass}">
1265
+ <i class="fas fa-${genderIcon} mr-2"></i>${horse.name}
1266
+ </h3>
1267
+ <button class="bg-red-600 hover:bg-red-700 text-white text-sm font-bold py-1 px-3 rounded transition race-horse-btn" data-horse-id="${horse.id}">
1268
+ Enter Race
1269
+ </button>
1270
+ </div>
1271
+ <div class="grid grid-cols-2 gap-2 mb-2">
1272
+ <div>
1273
+ <div class="text-xs text-gray-500 mb-1">Speed</div>
1274
+ <div class="flex items-center">
1275
+ <div class="stats-bar stat-speed" style="width: ${horse.effectiveSpeed * 6}px;"></div>
1276
+ <span class="text-xs ml-2 font-semibold">${horse.effectiveSpeed}</span>
1277
+ </div>
1278
+ </div>
1279
+ <div>
1280
+ <div class="text-xs text-gray-500 mb-1">Endurance</div>
1281
+ <div class="flex items-center">
1282
+ <div class="stats-bar stat-endurance" style="width: ${horse.effectiveEndurance * 6}px;"></div>
1283
+ <span class="text-xs ml-2 font-semibold">${horse.effectiveEndurance}</span>
1284
+ </div>
1285
+ </div>
1286
+ </div>
1287
+ <div class="text-xs">
1288
+ <span class="font-semibold">Training:</span> ${trainedTimes}/3 sessions completed
1289
+ </div>
1290
+ `;
1291
+
1292
+ horseCard.querySelector('.race-horse-btn').addEventListener('click', () => startRace(horse));
1293
+
1294
+ elements.racableHorses.appendChild(horseCard);
1295
+ });
1296
+
1297
+ if (elements.racableHorses.children.length === 0) {
1298
+ elements.racableHorses.innerHTML = '<p class="text-gray-500 italic">No horses available for racing. All horses are injured or you have no horses.</p>';
1299
+ }
1300
+
1301
+ elements.raceModal.style.display = 'flex';
1302
+ }
1303
+
1304
+ function startRace(playerHorse) {
1305
+ elements.raceModal.style.display = 'none';
1306
+
1307
+ // Generate 3-5 competitor horses based on player horse's stats
1308
+ const competitorCount = 3 + Math.floor(Math.random() * 3);
1309
+ const competitors = [];
1310
+
1311
+ for (let i = 0; i < competitorCount; i++) {
1312
+ // Create competitors with stats slightly varied from player's horse
1313
+ const speedVariation = Math.floor(Math.random() * 5) - 2;
1314
+ const enduranceVariation = Math.floor(Math.random() * 5) - 2;
1315
+
1316
+ competitors.push({
1317
+ id: Date.now() + Math.floor(Math.random() * 1000) + i,
1318
+ name: generateCompetitorName(),
1319
+ effectiveSpeed: Math.max(5, Math.min(20, playerHorse.effectiveSpeed + speedVariation)),
1320
+ effectiveEndurance: Math.max(5, Math.min(20, playerHorse.effectiveEndurance + enduranceVariation)),
1321
+ color: getRandomHorseColor(),
1322
+ position: i + 1
1323
+ });
1324
+ }
1325
+
1326
+ // Add player's horse to competitors
1327
+ const playerHorseCopy = {
1328
+ ...playerHorse,
1329
+ color: '#3b82f6', // Blue for player's horse
1330
+ position: 0
1331
+ };
1332
+ competitors.push(playerHorseCopy);
1333
+
1334
+ // Shuffle competitors
1335
+ const shuffledCompetitors = [...competitors].sort(() => Math.random() - 0.5);
1336
+
1337
+ // Display race track
1338
+ elements.raceTrack.innerHTML = '';
1339
+ elements.raceResultsList.innerHTML = '';
1340
+ elements.raceResults.classList.remove('hidden');
1341
+
1342
+ shuffledCompetitors.forEach((horse, index) => {
1343
+ const horseDiv = document.createElement('div');
1344
+ horseDiv.className = 'horse-runner';
1345
+ horseDiv.style.top = `${index * 18}px`;
1346
+ horseDiv.style.backgroundImage = `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="${encodeURIComponent(horse.color)}" d="M448 96V80c0-8.8-7.2-16-16-16h-16c-8.8 0-16 7.2-16 16v16h-32V80c0-8.8-7.2-16-16-16h-16c-8.8 0-16 7.2-16 16v16h-32V80c0-8.8-7.2-16-16-16h-16c-8.8 0-16 7.2-16 16v16h-32V80c0-8.8-7.2-16-16-16h-16c-8.8 0-16 7.2-16 16v16H96V80c0-8.8-7.2-16-16-16H64c-8.8 0-16 7.2-16 16v16H32C14.3 96 0 110.3 0 128v96c0 17.7 14.3 32 32 32h32v96c0 17.7 14.3 32 32 32h32v96c0 17.7 14.3 32 32 32h64c17.7 0 32-14.3 32-32v-96h32v96c0 17.7 14.3 32 32 32h64c17.7 0 32-14.3 32-32v-96h32v96c0 17.7 14.3 32 32 32h64c17.7 0 32-14.3 32-32v-96h32c17.7 0 32-14.3 32-32V256h32c17.7 0 32-14.3 32-32V128c0-17.7-14.3-32-32-32h-32z"/></svg>')`;
1347
+ horseDiv.id = `horse-${horse.id}`;
1348
+ horseDiv.dataset.horseId = horse.id;
1349
+ horseDiv.dataset.speed = horse.effectiveSpeed;
1350
+ horseDiv.dataset.endurance = horse.effectiveEndurance;
1351
+ horseDiv.dataset.currentSpeed = horse.effectiveSpeed;
1352
+ horseDiv.dataset.currentEndurance = horse.effectiveEndurance;
1353
+ horseDiv.dataset.lap = 0;
1354
+ horseDiv.dataset.finished = 0;
1355
+
1356
+ elements.raceTrack.appendChild(horseDiv);
1357
+ });
1358
+
1359
+ // Run the race simulation
1360
+ simulateRace(shuffledCompetitors, playerHorse);
1361
+ }
1362
+
1363
+ function simulateRace(competitors, playerHorse) {
1364
+ const totalLaps = 3;
1365
+ let finishedHorses = 0;
1366
+ const results = [];
1367
+ const raceInterval = setInterval(() => {
1368
+ competitors.forEach(horse => {
1369
+ if (horse.finished) return;
1370
+
1371
+ // Move horse based on current speed
1372
+ const horseElement = document.getElementById(`horse-${horse.id}`);
1373
+ if (!horseElement) return;
1374
+
1375
+ // Update lap if needed
1376
+ const currentPosition = parseFloat(getComputedStyle(horseElement).transform.split(',')[4]) || 0;
1377
+ const trackWidth = elements.raceTrack.offsetWidth - 100; // Rough width
1378
+ const lapWidth = trackWidth / totalLaps;
1379
+
1380
+ const newLap = Math.min(totalLaps, Math.floor(currentPosition / lapWidth) + 1);
1381
+
1382
+ if (newLap !== parseInt(horseElement.dataset.lap)) {
1383
+ horseElement.dataset.lap = newLap;
1384
+
1385
+ // Check if race is finished for this horse
1386
+ if (newLap >= totalLaps) {
1387
+ horseElement.dataset.finished = 1;
1388
+ horse.finished = true;
1389
+ finishedHorses++;
1390
+
1391
+ // Record result
1392
+ const position = finishedHouses;
1393
+ results.push({
1394
+ ...horse,
1395
+ position: finishedHorses
1396
+ });
1397
+
1398
+ // Show finish animation
1399
+ horseElement.style.animation = 'none';
1400
+ horseElement.style.transform = 'translateX(calc(100% - 100px))';
1401
+
1402
+ // Add to results list
1403
+ const resultItem = document.createElement('div');
1404
+ resultItem.className = 'flex items-center justify-between py-1 px-2';
1405
+ if (horse.id === playerHorse.id) {
1406
+ resultItem.className += ' bg-blue-50';
1407
+ }
1408
+
1409
+ resultItem.innerHTML = `
1410
+ <div class="flex items-center">
1411
+ <span class="font-bold text-lg mr-2">${finishedHorses}.</span>
1412
+ <span class="font-semibold">${horse.name}</span>
1413
+ </div>
1414
+ <div class="text-right">
1415
+ <span class="text-sm">Speed: ${horse.effectiveSpeed}, Endurance: ${horse.effectiveEndurance}</span>
1416
+ </div>
1417
+ `;
1418
+
1419
+ elements.raceResultsList.appendChild(resultItem);
1420
+
1421
+ // Check if race is over
1422
+ if (finishedHorses === competitors.length) {
1423
+ clearInterval(raceInterval);
1424
+ finalizeRace(results, playerHorse);
1425
+ }
1426
+
1427
+ return;
1428
+ }
1429
+ }
1430
+
1431
+ // Calculate speed adjustments based on endurance
1432
+ let currentEndurance = parseInt(horseElement.dataset.currentEndurance);
1433
+ let currentSpeed = parseInt(horseElement.dataset.currentSpeed);
1434
+ let speedMultiplier = 1;
1435
+
1436
+ if (currentEndurance <= 10) {
1437
+ speedMultiplier = 0.1;
1438
+ } else if (currentEndurance <= 25) {
1439
+ speedMultiplier = 0.25;
1440
+ } else if (currentEndurance <= 50) {
1441
+ speedMultiplier = 0.5;
1442
+ }
1443
+
1444
+ // Reduce endurance by speed value
1445
+ currentEndurance -= currentSpeed * 0.5;
1446
+
1447
+ // Don't let endurance go below 0
1448
+ if (currentEndurance < 1) currentEndurance = 1;
1449
+
1450
+ // Update element data
1451
+ horseElement.dataset.currentEndurance = Math.floor(currentEndurance);
1452
+ horseElement.dataset.currentSpeed = Math.floor(currentSpeed * speedMultiplier);
1453
+
1454
+ // Update animation duration based on current speed
1455
+ const animationDuration = Math.max(1, 10 - (currentSpeed * speedMultiplier * 0.5));
1456
+ horseElement.style.animationDuration = `${animationDuration}s`;
1457
+ });
1458
+ }, 100);
1459
+
1460
+ // Start animations
1461
+ competitors.forEach(horse => {
1462
+ const horseElement = document.getElementById(`horse-${horse.id}`);
1463
+ if (horseElement) {
1464
+ horseElement.style.animationName = 'raceHorse';
1465
+ horseElement.style.animationDuration = `${10 - (horse.effectiveSpeed * 0.5)}s`;
1466
+ horseElement.style.animationTimingFunction = 'linear';
1467
+ horseElement.style.animationFillMode = 'forwards';
1468
+ }
1469
+ });
1470
+ }
1471
+
1472
+ function finalizeRace(results, playerHorse) {
1473
+ // Find player's result
1474
+ const playerResult = results.find(r => r.id === playerHorse.id);
1475
+
1476
+ if (playerResult) {
1477
+ // Calculate prize money based on position
1478
+ let prize = 0;
1479
+
1480
+ switch(playerResult.position) {
1481
+ case 1: prize = 10000; break;
1482
+ case 2: prize = 5000; break;
1483
+ case 3: prize = 2500; break;
1484
+ default: prize = 1000;
1485
+ }
1486
+
1487
+ // Adjust prize by horse stats (higher stats = better prize)
1488
+ prize = Math.floor(prize * (1 + (playerHorse.effectiveSpeed + playerHorse.effectiveEndurance) / 40));
1489
+
1490
+ gameState.money += prize;
1491
+
1492
+ // Reset training count for this horse
1493
+ playerHorse.trainingCount = 0;
1494
+
1495
+ // Add game message
1496
+ addGameMessage(
1497
+ playerResult.position <= 3 ?
1498
+ `🎉 ${playerHorse.name} finished ${ordinalSuffix(playerResult.position)} and earned you $${prize.toLocaleString()}!` :
1499
+ `${playerHorse.name} finished ${ordinalSuffix(playerResult.position)} in the race. No prize money earned.`
1500
+ );
1501
+
1502
+ // Update UI
1503
+ updateUI();
1504
+ updateStableDisplay();
1505
+ }
1506
+ }
1507
+
1508
+ function openSell() {
1509
+ elements.sellableHorses.innerHTML = '';
1510
+
1511
+ gameState.stable.forEach(horse => {
1512
+ const horseCard = document.createElement('div');
1513
+ horseCard.className = 'bg-gray-50 rounded-lg p-3 border border-gray-300';
1514
+
1515
+ const genderClass = horse.gender === 'male' ? 'horse-male' : 'horse-female';
1516
+ const genderIcon = horse.gender === 'male' ? 'mars' : 'venus';
1517
+
1518
+ const canBreedExternally = horse.effectiveFertility > 0;
1519
+ const canOfferService = horse.gender === 'male' && canBreedExternally;
1520
+ const canSell = horse.gender === 'female' || !canOfferService;
1521
+
1522
+ horseCard.innerHTML = `
1523
+ <div class="flex justify-between items-center mb-2">
1524
+ <h3 class="font-semibold ${genderClass}">
1525
+ <i class="fas fa-${genderIcon} mr-2"></i>${horse.name}
1526
+ </h3>
1527
+ <div class="text-right">
1528
+ <div class="font-semibold">$${calculateHorseValue(horse).toLocaleString()}</div>
1529
+ </div>
1530
+ </div>
1531
+ <div class="grid grid-cols-2 gap-2 mb-3">
1532
+ <div>
1533
+ <div class="text-xs text-gray-500 mb-1">Speed</div>
1534
+ <div class="flex items-center">
1535
+ <div class="stats-bar stat-speed" style="width: ${horse.effectiveSpeed * 6}px;"></div>
1536
+ <span class="text-xs ml-2 font-semibold">${horse.effectiveSpeed}</span>
1537
+ </div>
1538
+ </div>
1539
+ <div>
1540
+ <div class="text-xs text-gray-500 mb-1">Endurance</div>
1541
+ <div class="flex items-center">
1542
+ <div class="stats-bar stat-endurance" style="width: ${horse.effectiveEndurance * 6}px;"></div>
1543
+ <span class="text-xs ml-2 font-semibold">${horse.effectiveEndurance}</span>
1544
+ </div>
1545
+ </div>
1546
+ </div>
1547
+ <div class="flex justify-between space-x-2">
1548
+ ${canSell ?
1549
+ `<button class="flex-1 bg-gray-600 hover:bg-gray-700 text-white text-sm font-bold py-1 px-3 rounded transition sell-horse-btn" data-horse-id="${horse.id}">
1550
+ Sell
1551
+ </button>` : ''
1552
+ }
1553
+ ${canOfferService ?
1554
+ `<button class="flex-1 bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-bold py-1 px-3 rounded transition offer-service-btn" data-horse-id="${horse.id}">
1555
+ Offer Breeding
1556
+ </button>` : ''
1557
+ }
1558
+ </div>
1559
+ `;
1560
+
1561
+ if (canSell) {
1562
+ horseCard.querySelector('.sell-horse-btn').addEventListener('click', () => sellHorse(horse));
1563
+ }
1564
+
1565
+ if (canOfferService) {
1566
+ horseCard.querySelector('.offer-service-btn').addEventListener('click', () => offerBreedingService(horse));
1567
+ }
1568
+
1569
+ elements.sellableHorses.appendChild(horseCard);
1570
+ });
1571
+
1572
+ elements.sellModal.style.display = 'flex';
1573
+ }
1574
+
1575
+ function sellHorse(horse) {
1576
+ const value = calculateHorseValue(horse);
1577
+ gameState.money += value;
1578
+ gameState.stable = gameState.stable.filter(h => h.id !== horse.id);
1579
+
1580
+ // If this horse was injured, remove from injured set
1581
+ if (gameState.injuredHorses.has(horse.id)) {
1582
+ gameState.injuredHorses.delete(horse.id);
1583
+ }
1584
+
1585
+ addGameMessage(`You sold ${horse.name} for $${value.toLocaleString()}.`);
1586
+
1587
+ updateUI();
1588
+ updateStableDisplay();
1589
+ elements.sellModal.style.display = 'none';
1590
+ }
1591
+
1592
+ function offerBreedingService(horse) {
1593
+ const earnings = Math.floor(calculateBreedingValue(horse));
1594
+ gameState.money += earnings;
1595
+
1596
+ // Reduce fertility by 25%
1597
+ horse.fertility = Math.max(0, horse.fertility - 25);
1598
+ horse.effectiveFertility = Math.max(0, Math.min(100, horse.fertility + (horse.traits.reduce((sum, t) => sum + (t.effect.fertility || 0), 0))));
1599
+
1600
+ addGameMessage(`You earned $${earnings.toLocaleString()} by offering ${horse.name} for breeding.`);
1601
+
1602
+ updateUI();
1603
+ updateStableDisplay();
1604
+ elements.sellModal.style.display = 'none';
1605
+ }
1606
+
1607
+ function nextDay() {
1608
+ gameState.day++;
1609
+
1610
+ // Heal injured horses
1611
+ gameState.injuredHorses = new Set();
1612
+
1613
+ // Reset training counts
1614
+ gameState.stable.forEach(horse => {
1615
+ horse.trainingCount = 0;
1616
+ });
1617
+
1618
+ // Generate new market horses
1619
+ generateMarketHorses();
1620
+
1621
+ addGameMessage(`A new day begins! All injuries healed and training limits reset.`);
1622
+
1623
+ updateUI();
1624
+ updateStableDisplay();
1625
+ }
1626
+
1627
+ function calculateHorseValue(horse) {
1628
+ // Base value on stats and traits
1629
+ let value = (horse.effectiveSpeed * 500) + (horse.effectiveEndurance * 400) + (horse.effectiveBeauty * 300);
1630
+
1631
+ // Adjust for fertility - higher fertility = higher value
1632
+ value *= 0.8 + (horse.effectiveFertility / 100);
1633
+
1634
+ // Add bonus for traits
1635
+ value *= 1 + (horse.traits.length * 0.1);
1636
+
1637
+ return Math.floor(value * 0.8); // Only worth 80% of "market value"
1638
+ }
1639
+
1640
+ function calculateBreedingValue(horse) {
1641
+ return Math.floor(calculateHorseValue(horse) * 0.25);
1642
+ }
1643
+
1644
+ function generateCompetitorName() {
1645
+ const prefixes = ['Thunder', 'Swift', 'Black', 'Silver', 'Golden', 'Midnight', 'Royal', 'Wild'];
1646
+ const suffixes = ['Runner', 'Hoof', 'Storm', 'Shadow', 'Dash', 'Flash', 'Wind'];
1647
+
1648
+ return `${prefixes[Math.floor(Math.random() * prefixes.length)]}'s ${suffixes[Math.floor(Math.random() * suffixes.length)]}`;
1649
+ }
1650
+
1651
+ function getRandomHorseColor() {
1652
+ const colors = [
1653
+ '#a855f7', // purple
1654
+ '#ef4444', // red
1655
+ '#10b981', // emerald
1656
+ '#f59e0b', // amber
1657
+ '#6366f1', // indigo
1658
+ '#ec4899', // pink
1659
+ '#14b8a6', // teal
1660
+ '#f97316' // orange
1661
+ ];
1662
+
1663
+ return colors[Math.floor(Math.random() * colors.length)];
1664
+ }
1665
+
1666
+ function ordinalSuffix(num) {
1667
+ const j = num % 10;
1668
+ const k = num % 100;
1669
+ if (j == 1 && k != 11) {
1670
+ return num + "st";
1671
+ }
1672
+ if (j == 2 && k != 12) {
1673
+ return num + "nd";
1674
+ }
1675
+ if (j == 3 && k != 13) {
1676
+ return num + "rd";
1677
+ }
1678
+ return num + "th";
1679
+ }
1680
+
1681
+ function addGameMessage(message) {
1682
+ const messageElement = document.createElement('p');
1683
+ messageElement.className = 'text-gray-700 mb-1';
1684
+ messageElement.textContent = `Day ${gameState.day}: ${message}`;
1685
+
1686
+ elements.gameMessages.insertBefore(messageElement, elements.gameMessages.firstChild);
1687
+
1688
+ // Limit to 10 messages
1689
+ if (elements.gameMessages.children.length > 10) {
1690
+ elements.gameMessages.removeChild(elements.gameMessages.lastChild);
1691
+ }
1692
+ }
1693
+
1694
+ function showNotification(title, content) {
1695
+ elements.notificationContent.innerHTML = `
1696
+ <h3 class="text-xl font-bold mb-2">${title}</h3>
1697
+ <div>${content}</div>
1698
+ `;
1699
+ elements.notificationModal.style.display = 'flex';
1700
+ }
1701
+
1702
+ function updateUpcomingRaces() {
1703
+ elements.upcomingRaces.innerHTML = `
1704
+ <h3 class="text-lg font-semibold text-gray-700 mb-2">Upcoming Races</h3>
1705
+ <div class="bg-gray-100 p-3 rounded-lg">
1706
+ <p class="text-gray-700">Race Day ${gameState.day + (3 - (gameState.day % 3))} - Prize pool: $12,000</p>
1707
+ </div>
1708
+ `;
1709
+ }
1710
+ </script>
1711
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=furuknap/stablemasters" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1712
+ </html>
prompts.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ I want to create a simple horse breeding, training, and racing game. The game loop is to buy horses and breed them. Each breeding has a chance of getting a trait, positive or negative, from the parents. Horses have four stats: - Speed - Endurance - Beauty - Fertility In addition, a horse has a gender. These stats have a genetic value and a modified value. The genetic value is what can be obtained by breeding. In addition, traits can modify stats. For example, a "Slow but steady" trait can decrease speed but increase endurance. Training can be done three times between races. Training has a chance of injuring the horse, in which case they cannot participate in the next race. In races, only speed and endurance are used. During the race, each horse has a temporary endurance and speed value that starts at the endurance and speed values. The temporary endurance is decreased by the temporary speed value each lap. When the temporary endurance reaches 50%, the speed reduces by 25%. When the temporary endurance reaches 25%, the speed reduces by another 25%, and when the temporary endurance reaches <10%, the speed reduces to 10% too, and no further reduction in endurance or speed happens until the end of the race. For breeding, each horse has a fertility that starts at 100% and decreases by 25% each time it is bred. The chance that its genetic value and traits are passed on depends on the remaining fertility. When fertility reaches 0%, the horse can no longer breed. Beauty is not required for the first version of the game. The player starts with $10K and must buy a horse on a market. There will be three horses each round that the player can purchase, of various values. At least one horse is always available below the player's remaining money. The cost of a horse should be a reflection of its stats. The player can earn more money by winning races or selling offspring or breeding services. The value of offspring and breeding services depends on the stats. The value should in total yield around 1/4 of the value of the horse. When choosing breeding, a player can also breed with their own horses if they have eligible male and female hoses. If the player has a female horse only, they must pay someone to mate, around 1/4 of the male horse's value, but the player gets offspring in return. If the player has a male horse only, they immediately get paid around 1/4 of the value of their horse.