Sergidev commited on
Commit
570a1ab
·
1 Parent(s): cc27e32
Files changed (7) hide show
  1. Dockerfile +1 -1
  2. README.md +1 -1
  3. app.py +35 -18
  4. requirements.txt +2 -1
  5. static/css/style.css +34 -5
  6. static/js/game.js +113 -102
  7. templates/index.html +38 -30
Dockerfile CHANGED
@@ -7,4 +7,4 @@ RUN pip install --no-cache-dir -r requirements.txt
7
 
8
  COPY . .
9
 
10
- CMD ["gunicorn", "--bind", "0.0.0.0:7860", "app:app"]
 
7
 
8
  COPY . .
9
 
10
+ CMD ["gunicorn", "--bind", "0.0.0.0:7860", "app:app"]
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
  title: 1kcoinsA
3
- emoji: 📚
4
  colorFrom: pink
5
  colorTo: indigo
6
  sdk: docker
 
1
  ---
2
  title: 1kcoinsA
3
+ emoji: 💰
4
  colorFrom: pink
5
  colorTo: indigo
6
  sdk: docker
app.py CHANGED
@@ -1,10 +1,18 @@
1
  from flask import Flask, render_template, jsonify, request
 
 
 
 
2
  import json
3
  import random
4
  import os
5
 
6
  app = Flask(__name__)
7
 
 
 
 
 
8
  # Load coins from JSON file
9
  def load_coins():
10
  if not os.path.exists('coins.json'):
@@ -23,15 +31,19 @@ def index():
23
 
24
  @app.route('/api/coins')
25
  def get_coins():
26
- return jsonify(load_coins())
 
 
27
 
28
  @app.route('/api/flip', methods=['POST'])
29
  def flip_coin():
30
  data = request.json
31
  coin_id = data['coinId']
32
- coins = load_coins()
 
 
33
  coin = next((c for c in coins if c['id'] == coin_id), None)
34
-
35
  if coin and random.random() < coin['winrate']:
36
  return jsonify({'result': 'heads', 'value': coin['value']})
37
  else:
@@ -39,14 +51,17 @@ def flip_coin():
39
 
40
  @app.route('/api/mint', methods=['POST'])
41
  def mint_coin():
42
- coins = load_coins()
 
 
43
  new_id = max(coin['id'] for coin in coins) + 1
44
-
45
- # Algorithm to balance cost, price, and value
46
- winrate = random.uniform(0.1, 0.9)
47
- value = random.uniform(0.1, 2.0)
48
- price = int(max(1, (winrate * value * 100) ** 1.5))
49
-
 
50
  new_coin = {
51
  'id': new_id,
52
  'color': f'#{random.randint(0, 0xFFFFFF):06x}',
@@ -54,33 +69,35 @@ def mint_coin():
54
  'value': round(value, 2),
55
  'winrate': round(winrate, 2)
56
  }
57
-
58
  coins.append(new_coin)
 
 
59
  save_coins(coins)
60
  return jsonify(new_coin)
61
 
62
  @app.route('/api/leaderboard', methods=['GET', 'POST'])
63
  def leaderboard():
64
  leaderboard_file = 'leaderboard.json'
65
-
66
  if request.method == 'POST':
67
  data = request.json
68
  initials = data['initials']
69
  score = data['score']
70
-
71
  try:
72
  with open(leaderboard_file, 'r') as f:
73
  leaderboard = json.load(f)
74
  except FileNotFoundError:
75
  leaderboard = []
76
-
77
  leaderboard.append({'initials': initials, 'score': score})
78
  leaderboard.sort(key=lambda x: x['score'], reverse=True)
79
  leaderboard = leaderboard[:10] # Keep only top 10
80
-
81
  with open(leaderboard_file, 'w') as f:
82
  json.dump(leaderboard, f)
83
-
84
  return jsonify({'success': True})
85
  else:
86
  try:
@@ -88,8 +105,8 @@ def leaderboard():
88
  leaderboard = json.load(f)
89
  except FileNotFoundError:
90
  leaderboard = []
91
-
92
  return jsonify(leaderboard)
93
 
94
  if __name__ == '__main__':
95
- app.run(debug=True, host='0.0.0.0', port=7860)
 
1
  from flask import Flask, render_template, jsonify, request
2
+
3
+ from flask import session
4
+ from flask_session import Session
5
+
6
  import json
7
  import random
8
  import os
9
 
10
  app = Flask(__name__)
11
 
12
+ app.config['SECRET_KEY'] = os.urandom(24)
13
+ app.config['SESSION_TYPE'] = 'filesystem'
14
+ Session(app)
15
+
16
  # Load coins from JSON file
17
  def load_coins():
18
  if not os.path.exists('coins.json'):
 
31
 
32
  @app.route('/api/coins')
33
  def get_coins():
34
+ if 'coins' not in session:
35
+ session['coins'] = load_coins()
36
+ return jsonify(session['coins'])
37
 
38
  @app.route('/api/flip', methods=['POST'])
39
  def flip_coin():
40
  data = request.json
41
  coin_id = data['coinId']
42
+ if 'coins' not in session:
43
+ session['coins'] = load_coins()
44
+ coins = session['coins']
45
  coin = next((c for c in coins if c['id'] == coin_id), None)
46
+
47
  if coin and random.random() < coin['winrate']:
48
  return jsonify({'result': 'heads', 'value': coin['value']})
49
  else:
 
51
 
52
  @app.route('/api/mint', methods=['POST'])
53
  def mint_coin():
54
+ if 'coins' not in session:
55
+ session['coins'] = load_coins()
56
+ coins = session['coins']
57
  new_id = max(coin['id'] for coin in coins) + 1
58
+
59
+ # More balanced algorithm for cost, price, and value
60
+ base_price = 4 + (new_id - 1) * 2 # Base price increases with each new coin
61
+ winrate = random.uniform(0.42, 0.98) # More balanced win rate
62
+ value = random.uniform(0.5, 1.5) * (base_price / 4) # Value scales with base price
63
+ price = int(base_price * (1 + random.uniform(-0.1, 0.1))) # Price varies slightly around base
64
+
65
  new_coin = {
66
  'id': new_id,
67
  'color': f'#{random.randint(0, 0xFFFFFF):06x}',
 
69
  'value': round(value, 2),
70
  'winrate': round(winrate, 2)
71
  }
72
+
73
  coins.append(new_coin)
74
+ session['coins'] = coins
75
+ session.modified = True
76
  save_coins(coins)
77
  return jsonify(new_coin)
78
 
79
  @app.route('/api/leaderboard', methods=['GET', 'POST'])
80
  def leaderboard():
81
  leaderboard_file = 'leaderboard.json'
82
+
83
  if request.method == 'POST':
84
  data = request.json
85
  initials = data['initials']
86
  score = data['score']
87
+
88
  try:
89
  with open(leaderboard_file, 'r') as f:
90
  leaderboard = json.load(f)
91
  except FileNotFoundError:
92
  leaderboard = []
93
+
94
  leaderboard.append({'initials': initials, 'score': score})
95
  leaderboard.sort(key=lambda x: x['score'], reverse=True)
96
  leaderboard = leaderboard[:10] # Keep only top 10
97
+
98
  with open(leaderboard_file, 'w') as f:
99
  json.dump(leaderboard, f)
100
+
101
  return jsonify({'success': True})
102
  else:
103
  try:
 
105
  leaderboard = json.load(f)
106
  except FileNotFoundError:
107
  leaderboard = []
108
+
109
  return jsonify(leaderboard)
110
 
111
  if __name__ == '__main__':
112
+ app.run(debug=True, host='0.0.0.0', port=7860)
requirements.txt CHANGED
@@ -1,3 +1,4 @@
1
  Flask==2.0.3
 
2
  Werkzeug==2.0.3
3
- gunicorn==20.1.0
 
1
  Flask==2.0.3
2
+ Flask-Session==0.4.0
3
  Werkzeug==2.0.3
4
+ gunicorn==20.1.0
static/css/style.css CHANGED
@@ -73,7 +73,7 @@ body {
73
  }
74
 
75
  #mint-button {
76
- background-color: #4CAF50;
77
  border: none;
78
  color: white;
79
  padding: 10px 20px;
@@ -107,11 +107,18 @@ body {
107
  }
108
 
109
  #leaderboard table {
 
 
 
 
 
 
110
  width: 100%;
111
  border-collapse: collapse;
112
  }
113
 
114
- #leaderboard th, #leaderboard td {
 
115
  border: 1px solid white;
116
  padding: 5px;
117
  text-align: center;
@@ -126,8 +133,9 @@ body {
126
  font-size: 16px;
127
  }
128
 
129
- #submit-score, #play-again {
130
- background-color: #4CAF50;
 
131
  border: none;
132
  color: white;
133
  padding: 10px 20px;
@@ -138,4 +146,25 @@ body {
138
  margin: 4px 2px;
139
  cursor: pointer;
140
  border-radius: 5px;
141
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  }
74
 
75
  #mint-button {
76
+ background-color: #4caf50;
77
  border: none;
78
  color: white;
79
  padding: 10px 20px;
 
107
  }
108
 
109
  #leaderboard table {
110
+ background-color: rgba(255, 255, 255, 0.1);
111
+ color: white;
112
+ margin-bottom: 20px;
113
+ max-height: 300px;
114
+ overflow-y: auto;
115
+
116
  width: 100%;
117
  border-collapse: collapse;
118
  }
119
 
120
+ #leaderboard th,
121
+ #leaderboard td {
122
  border: 1px solid white;
123
  padding: 5px;
124
  text-align: center;
 
133
  font-size: 16px;
134
  }
135
 
136
+ #submit-score,
137
+ #play-again {
138
+ background-color: #4caf50;
139
  border: none;
140
  color: white;
141
  padding: 10px 20px;
 
146
  margin: 4px 2px;
147
  cursor: pointer;
148
  border-radius: 5px;
149
+ }
150
+
151
+ .coin-tooltip {
152
+ position: absolute;
153
+ background-color: rgba(0, 0, 0, 0.8);
154
+ color: white;
155
+ padding: 10px;
156
+ border-radius: 5px;
157
+ font-size: 14px;
158
+ z-index: 1000;
159
+ pointer-events: none;
160
+ opacity: 0;
161
+ transition: opacity 0.3s ease-in-out;
162
+ }
163
+
164
+ .shop-item:hover .coin-tooltip {
165
+ opacity: 1;
166
+ }
167
+
168
+ .coin-tooltip p {
169
+ margin: 5px 0;
170
+ }
static/js/game.js CHANGED
@@ -4,89 +4,91 @@ let currentCoin = 1;
4
  let coins = [];
5
 
6
  function updateStats() {
7
- document.getElementById('balance').textContent = `$${balance.toFixed(2)}`;
8
- document.getElementById('flips-left').textContent = flipsLeft;
9
  }
10
-
11
  function updateShop() {
12
- const shop = document.getElementById('shop');
13
- shop.innerHTML = '';
14
-
15
- coins.forEach(coin => {
16
- const coinElement = document.createElement('div');
17
- coinElement.className = 'shop-item';
18
- coinElement.innerHTML = `
19
- <div class="coin-icon ${coin.id === currentCoin ? 'selected' : ''}"
20
  style="background-color: ${coin.color};"
21
- onclick="selectCoin(${coin.id})"></div>
 
 
 
22
  <div>$${coin.price}</div>
23
  `;
24
- shop.appendChild(coinElement);
25
- });
26
-
27
- const mintButton = document.createElement('button');
28
- mintButton.id = 'mint-button';
29
- mintButton.textContent = '🎲 Mint ($4)';
30
- mintButton.onclick = mintCoin;
31
- shop.appendChild(mintButton);
32
  }
33
 
34
  function selectCoin(id) {
35
- const coin = coins.find(c => c.id === id);
36
- if (coin && balance >= coin.price) {
37
- balance -= coin.price;
38
- currentCoin = id;
39
- updateStats();
40
- updateShop();
41
- }
42
  }
43
 
44
  async function flipCoin() {
45
- if (flipsLeft > 0) {
46
- flipsLeft--;
47
- const response = await fetch('/api/flip', {
48
- method: 'POST',
49
- headers: {
50
- 'Content-Type': 'application/json',
51
- },
52
- body: JSON.stringify({ coinId: currentCoin }),
53
- });
54
- const result = await response.json();
55
-
56
- const coin = document.getElementById('coin');
57
- coin.style.transform = 'rotateY(720deg)';
58
- setTimeout(() => {
59
- coin.style.transform = 'rotateY(0deg)';
60
- if (result.result === 'heads') {
61
- balance += result.value;
62
- coin.textContent = 'H';
63
- } else {
64
- coin.textContent = 'T';
65
- }
66
- updateStats();
67
- }, 500);
68
-
69
- if (flipsLeft === 0) {
70
- setTimeout(gameOver, 1000);
71
- }
72
  }
 
73
  }
74
 
75
  async function mintCoin() {
76
- if (balance >= 4) {
77
- balance -= 4;
78
- const response = await fetch('/api/mint', { method: 'POST' });
79
- const newCoin = await response.json();
80
- coins.push(newCoin);
81
- updateStats();
82
- updateShop();
83
- }
84
  }
85
 
86
  async function gameOver() {
87
- const gameOverScreen = document.createElement('div');
88
- gameOverScreen.id = 'game-over';
89
- gameOverScreen.innerHTML = `
90
  <h2>Game Over</h2>
91
  <p>Your final balance: $${balance.toFixed(2)}</p>
92
  <div id="leaderboard"></div>
@@ -96,63 +98,72 @@ async function gameOver() {
96
  </form>
97
  <button id="play-again">Play Again</button>
98
  `;
99
- document.body.appendChild(gameOverScreen);
100
-
101
- document.getElementById('initials-form').onsubmit = submitScore;
102
- document.getElementById('play-again').onclick = resetGame;
103
-
104
- await updateLeaderboard();
105
  }
106
 
107
  async function submitScore(event) {
108
- event.preventDefault();
109
- const initials = document.getElementById('initials-input').value.toUpperCase();
110
- if (initials) {
111
- await fetch('/api/leaderboard', {
112
- method: 'POST',
113
- headers: {
114
- 'Content-Type': 'application/json',
115
- },
116
- body: JSON.stringify({ initials, score: balance }),
117
- });
118
- await updateLeaderboard();
119
- }
 
 
120
  }
121
 
122
  async function updateLeaderboard() {
123
- const response = await fetch('/api/leaderboard');
124
- const leaderboard = await response.json();
125
- const leaderboardElement = document.getElementById('leaderboard');
126
- leaderboardElement.innerHTML = `
127
  <h3>Leaderboard</h3>
128
  <table>
129
  <tr><th>Rank</th><th>Initials</th><th>Score</th></tr>
130
- ${leaderboard.map((entry, index) => `
 
 
131
  <tr>
132
  <td>${index + 1}</td>
133
  <td>${entry.initials}</td>
134
  <td>$${entry.score.toFixed(2)}</td>
135
  </tr>
136
- `).join('')}
 
 
137
  </table>
138
  `;
139
  }
140
-
141
  function resetGame() {
142
- balance = 10;
143
- flipsLeft = 1000;
144
- currentCoin = 1;
145
- updateStats();
146
- updateShop();
147
- document.getElementById('game-over').remove();
 
 
148
  }
149
 
150
  async function initGame() {
151
- const response = await fetch('/api/coins');
152
- coins = await response.json();
153
- updateStats();
154
- updateShop();
155
- document.getElementById('coin').onclick = flipCoin;
156
  }
157
 
158
- initGame();
 
 
 
4
  let coins = [];
5
 
6
  function updateStats() {
7
+ document.getElementById("balance").textContent = `$${balance.toFixed(2)}`;
8
+ document.getElementById("flips-left").textContent = flipsLeft;
9
  }
 
10
  function updateShop() {
11
+ const shop = document.getElementById("shop");
12
+ shop.innerHTML = "";
13
+
14
+ coins.forEach((coin) => {
15
+ const coinElement = document.createElement("div");
16
+ coinElement.className = "shop-item";
17
+ coinElement.innerHTML = `
18
+ <div class="coin-icon ${coin.id === currentCoin ? "selected" : ""}"
19
  style="background-color: ${coin.color};"
20
+ onclick="selectCoin(${coin.id})"
21
+ title="Win Rate: ${(coin.winrate * 100).toFixed(1)}%
22
+ Value: $${coin.value.toFixed(2)}
23
+ Price: $${coin.price}"></div>
24
  <div>$${coin.price}</div>
25
  `;
26
+ shop.appendChild(coinElement);
27
+ });
28
+
29
+ const mintButton = document.createElement("button");
30
+ mintButton.id = "mint-button";
31
+ mintButton.textContent = "🎲 Mint ($4)";
32
+ mintButton.onclick = mintCoin;
33
+ shop.appendChild(mintButton);
34
  }
35
 
36
  function selectCoin(id) {
37
+ const coin = coins.find((c) => c.id === id);
38
+ if (coin && balance >= coin.price) {
39
+ balance -= coin.price;
40
+ currentCoin = id;
41
+ updateStats();
42
+ updateShop();
43
+ }
44
  }
45
 
46
  async function flipCoin() {
47
+ if (flipsLeft > 0) {
48
+ flipsLeft--;
49
+ const response = await fetch("/api/flip", {
50
+ method: "POST",
51
+ headers: {
52
+ "Content-Type": "application/json",
53
+ },
54
+ body: JSON.stringify({ coinId: currentCoin }),
55
+ });
56
+ const result = await response.json();
57
+
58
+ const coin = document.getElementById("coin");
59
+ coin.style.transform = "rotateY(720deg)";
60
+ setTimeout(() => {
61
+ coin.style.transform = "rotateY(0deg)";
62
+ if (result.result === "heads") {
63
+ balance += result.value;
64
+ coin.textContent = "H";
65
+ } else {
66
+ coin.textContent = "T";
67
+ }
68
+ updateStats();
69
+ }, 500);
70
+
71
+ if (flipsLeft === 0) {
72
+ setTimeout(gameOver, 1000);
 
73
  }
74
+ }
75
  }
76
 
77
  async function mintCoin() {
78
+ if (balance >= 4) {
79
+ balance -= 4;
80
+ const response = await fetch("/api/mint", { method: "POST" });
81
+ const newCoin = await response.json();
82
+ coins.push(newCoin);
83
+ updateStats();
84
+ updateShop();
85
+ }
86
  }
87
 
88
  async function gameOver() {
89
+ const gameOverScreen = document.createElement("div");
90
+ gameOverScreen.id = "game-over";
91
+ gameOverScreen.innerHTML = `
92
  <h2>Game Over</h2>
93
  <p>Your final balance: $${balance.toFixed(2)}</p>
94
  <div id="leaderboard"></div>
 
98
  </form>
99
  <button id="play-again">Play Again</button>
100
  `;
101
+ document.body.appendChild(gameOverScreen);
102
+
103
+ document.getElementById("initials-form").onsubmit = submitScore;
104
+ document.getElementById("play-again").onclick = resetGame;
105
+
106
+ await updateLeaderboard();
107
  }
108
 
109
  async function submitScore(event) {
110
+ event.preventDefault();
111
+ const initials = document
112
+ .getElementById("initials-input")
113
+ .value.toUpperCase();
114
+ if (initials) {
115
+ await fetch("/api/leaderboard", {
116
+ method: "POST",
117
+ headers: {
118
+ "Content-Type": "application/json",
119
+ },
120
+ body: JSON.stringify({ initials, score: balance }),
121
+ });
122
+ await updateLeaderboard();
123
+ }
124
  }
125
 
126
  async function updateLeaderboard() {
127
+ const response = await fetch("/api/leaderboard");
128
+ const leaderboard = await response.json();
129
+ const leaderboardElement = document.getElementById("leaderboard");
130
+ leaderboardElement.innerHTML = `
131
  <h3>Leaderboard</h3>
132
  <table>
133
  <tr><th>Rank</th><th>Initials</th><th>Score</th></tr>
134
+ ${leaderboard
135
+ .map(
136
+ (entry, index) => `
137
  <tr>
138
  <td>${index + 1}</td>
139
  <td>${entry.initials}</td>
140
  <td>$${entry.score.toFixed(2)}</td>
141
  </tr>
142
+ `,
143
+ )
144
+ .join("")}
145
  </table>
146
  `;
147
  }
 
148
  function resetGame() {
149
+ balance = 10;
150
+ flipsLeft = 1000;
151
+ currentCoin = 1;
152
+ coins = []; // Reset coins array
153
+ initGame(); // Re-initialize the game to get initial coin state
154
+ updateStats();
155
+ updateShop();
156
+ document.getElementById("game-over").remove();
157
  }
158
 
159
  async function initGame() {
160
+ const response = await fetch("/api/coins");
161
+ coins = await response.json();
162
+ updateStats();
163
+ updateShop();
164
+ document.getElementById("coin").onclick = flipCoin;
165
  }
166
 
167
+ initGame();
168
+
169
+ console.log("Game initialized successfully");
templates/index.html CHANGED
@@ -1,32 +1,40 @@
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>1kcoins</title>
7
- <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
8
- </head>
9
- <body>
10
- <div id="stats">
11
- <div>Balance: <span id="balance">$10.00</span></div>
12
- <div>Flips left: <span id="flips-left">1000</span></div>
13
- </div>
14
- <div id="game-area">
15
- <div id="coin">Flip</div>
16
- </div>
17
- <div id="shop"></div>
18
-
19
- <div id="game-over" style="display: none;">
20
- <h2>Game Over</h2>
21
- <p>Your final balance: $<span id="final-balance"></span></p>
22
- <div id="leaderboard"></div>
23
- <form id="initials-form">
24
- <input type="text" id="initials-input" maxlength="3" placeholder="Enter your initials">
25
- <button type="submit" id="submit-score">Submit Score</button>
26
- </form>
27
- <button id="play-again">Play Again</button>
28
- </div>
29
 
30
- <script src="{{ url_for('static', filename='js/script.js') }}"></script>
31
- </body>
32
- </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>1kcoins</title>
7
+ <link
8
+ rel="stylesheet"
9
+ href="{{ url_for('static', filename='css/style.css') }}"
10
+ />
11
+ </head>
12
+ <body>
13
+ <div id="stats">
14
+ <div>Balance: <span id="balance">$10.00</span></div>
15
+ <div>Flips left: <span id="flips-left">1000</span></div>
16
+ </div>
17
+ <div id="game-area">
18
+ <div id="coin">Flip</div>
19
+ </div>
20
+ <div id="shop"></div>
 
 
 
 
 
 
 
 
21
 
22
+ <div id="game-over" style="display: none">
23
+ <h2>Game Over</h2>
24
+ <p>Your final balance: $<span id="final-balance"></span></p>
25
+ <div id="leaderboard"></div>
26
+ <form id="initials-form">
27
+ <input
28
+ type="text"
29
+ id="initials-input"
30
+ maxlength="3"
31
+ placeholder="Enter your initials"
32
+ />
33
+ <button type="submit" id="submit-score">Submit Score</button>
34
+ </form>
35
+ <button id="play-again">Play Again</button>
36
+ </div>
37
+
38
+ <script src="{{ url_for('static', filename='js/game.js') }}"></script>
39
+ </body>
40
+ </html>