miya3333 commited on
Commit
6a7997c
·
verified ·
1 Parent(s): 99d898e

Upload 5 files

Browse files
Files changed (5) hide show
  1. app.py +15 -0
  2. index.html +18 -18
  3. run.sh +5 -0
  4. script.js +177 -0
  5. style.css +65 -18
app.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import http.server
2
+ import socketserver
3
+ import os
4
+
5
+ # Define the port Spaces expects applications to run on
6
+ PORT = int(os.environ.get("PORT", 7860))
7
+ DIRECTORY = "."
8
+
9
+ class Handler(http.server.SimpleHTTPRequestHandler):
10
+ def __init__(self, *args, **kwargs):
11
+ super().__init__(*args, directory=DIRECTORY, **kwargs)
12
+
13
+ with socketserver.TCPServer(("", PORT), Handler) as httpd:
14
+ print(f"Serving directory {DIRECTORY} at port {PORT}")
15
+ httpd.serve_forever()
index.html CHANGED
@@ -1,19 +1,19 @@
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="ja">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>数字を発見するゲーム</title>
6
+ <link rel="stylesheet" href="style.css">
7
+ </head>
8
+ <body>
9
+ <div class="container">
10
+ <div id="gameInfo">
11
+ 探す数字: <span id="target">--</span>
12
+ </div>
13
+ <button id="startBtn">ゲーム開始</button>
14
+ <canvas id="gameCanvas" width="800" height="600"></canvas>
15
+ <div id="message"></div>
16
+ </div>
17
+ <script src="script.js"></script>
18
+ </body>
19
  </html>
run.sh ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ ```bash
2
+ #!/bin/bash
3
+
4
+ # Execute the Python script
5
+ python app.py
script.js ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function(){
2
+ const canvas = document.getElementById('gameCanvas');
3
+ const ctx = canvas.getContext('2d');
4
+ const targetSpan = document.getElementById('target');
5
+ const startBtn = document.getElementById('startBtn');
6
+ const messageDiv = document.getElementById('message');
7
+ let items = [];
8
+ let remaining = [];
9
+ let currentTarget = null;
10
+ const count = 30;
11
+ const fontSize = 24;
12
+ ctx.textBaseline = 'alphabetic';
13
+ ctx.font = fontSize + 'px sans-serif';
14
+
15
+ function isOverlapping(x, y, w, h, arr) {
16
+ return arr.some(item => {
17
+ return x < item.x + item.width &&
18
+ x + w > item.x &&
19
+ y - h < item.y &&
20
+ y > item.y - item.height;
21
+ });
22
+ }
23
+
24
+ function initGame() {
25
+ items = [];
26
+ messageDiv.textContent = '';
27
+ messageDiv.className = '';
28
+ targetSpan.textContent = '--';
29
+ for (let i = 1; i <= count; i++) {
30
+ const text = i.toString();
31
+ const metrics = ctx.measureText(text);
32
+ const width = metrics.width;
33
+ const height = fontSize;
34
+ let x, y, attempts = 0;
35
+ do {
36
+ x = Math.random() * (canvas.width - width);
37
+ y = Math.random() * (canvas.height - height) + height;
38
+ attempts++;
39
+ if (attempts > 1000) break;
40
+ } while (isOverlapping(x, y, width, height, items));
41
+ const angle = Math.random() * 2 * Math.PI;
42
+ items.push({ num: i, x, y, width, height, angle });
43
+ }
44
+ remaining = items.slice();
45
+ drawAll();
46
+ pickNext();
47
+ }
48
+
49
+ function drawAll() {
50
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
51
+ ctx.fillStyle = '#000';
52
+ remaining.forEach(item => {
53
+ ctx.save();
54
+ const cx = item.x + item.width / 2;
55
+ const cy = item.y - item.height / 2;
56
+ ctx.translate(cx, cy);
57
+ ctx.rotate(item.angle);
58
+ ctx.fillText(item.num, -item.width / 2, item.height / 2);
59
+ ctx.restore();
60
+ });
61
+ }
62
+
63
+ function pickNext() {
64
+ if (remaining.length === 0) {
65
+ currentTarget = null;
66
+ targetSpan.textContent = '--';
67
+ messageDiv.className = 'clear';
68
+ messageDiv.textContent = 'ゲームクリア!';
69
+ return;
70
+ }
71
+ const idx = Math.floor(Math.random() * remaining.length);
72
+ currentTarget = remaining[idx].num;
73
+ targetSpan.textContent = currentTarget;
74
+ }
75
+
76
+ function repositionItems() {
77
+ const newItems = [];
78
+ remaining.forEach(orig => {
79
+ const { num, width, height } = orig;
80
+ let x, y, attempts = 0;
81
+ do {
82
+ x = Math.random() * (canvas.width - width);
83
+ y = Math.random() * (canvas.height - height) + height;
84
+ attempts++;
85
+ if (attempts > 1000) break;
86
+ } while (isOverlapping(x, y, width, height, newItems));
87
+ const angle = Math.random() * 2 * Math.PI;
88
+ newItems.push({ num, x, y, width, height, angle });
89
+ });
90
+ return newItems;
91
+ }
92
+
93
+ function animateReposition(oldItems, newItems, duration, callback) {
94
+ const startTime = performance.now();
95
+ function animate(time) {
96
+ const t = Math.min((time - startTime) / duration, 1);
97
+ remaining = oldItems.map((oldItem, i) => {
98
+ const newItem = newItems[i];
99
+ return {
100
+ num: oldItem.num,
101
+ width: oldItem.width,
102
+ height: oldItem.height,
103
+ x: oldItem.x + (newItem.x - oldItem.x) * t,
104
+ y: oldItem.y + (newItem.y - oldItem.y) * t,
105
+ angle: oldItem.angle + (newItem.angle - oldItem.angle) * t
106
+ };
107
+ });
108
+ drawAll();
109
+ if (t < 1) requestAnimationFrame(animate);
110
+ else callback();
111
+ }
112
+ requestAnimationFrame(animate);
113
+ }
114
+
115
+ function drawRedCircle(item) {
116
+ const cx = item.x + item.width / 2;
117
+ const cy = item.y - item.height / 2;
118
+ const r = Math.max(item.width, item.height) / 2 + 5;
119
+ ctx.strokeStyle = 'red';
120
+ ctx.lineWidth = 3;
121
+ ctx.beginPath();
122
+ ctx.arc(cx, cy, r, 0, 2 * Math.PI);
123
+ ctx.stroke();
124
+ }
125
+
126
+ function drawRedCross(item) {
127
+ const x1 = item.x;
128
+ const y1 = item.y - item.height;
129
+ const x2 = item.x + item.width;
130
+ const y2 = item.y;
131
+ ctx.strokeStyle = 'red';
132
+ ctx.lineWidth = 3;
133
+ ctx.beginPath();
134
+ ctx.moveTo(x1, y1);
135
+ ctx.lineTo(x2, y2);
136
+ ctx.moveTo(x1, y2);
137
+ ctx.lineTo(x2, y1);
138
+ ctx.stroke();
139
+ }
140
+
141
+ canvas.addEventListener('click', e => {
142
+ if (currentTarget === null) return;
143
+ const rect = canvas.getBoundingClientRect();
144
+ const clickX = e.clientX - rect.left;
145
+ const clickY = e.clientY - rect.top;
146
+ for (let i = 0; i < remaining.length; i++) {
147
+ const item = remaining[i];
148
+ if (clickX >= item.x && clickX <= item.x + item.width &&
149
+ clickY <= item.y && clickY >= item.y - item.height) {
150
+ if (item.num === currentTarget) {
151
+ messageDiv.className = 'correct';
152
+ messageDiv.textContent = '正解!';
153
+ drawAll();
154
+ drawRedCircle(item);
155
+ setTimeout(() => {
156
+ remaining.splice(i, 1);
157
+ const oldItems = remaining.map(it => ({ ...it }));
158
+ const newItems = repositionItems();
159
+ animateReposition(oldItems, newItems, 1000, () => {
160
+ remaining = newItems;
161
+ pickNext();
162
+ });
163
+ }, 500);
164
+ } else {
165
+ messageDiv.className = 'wrong';
166
+ messageDiv.textContent = '違うよ!';
167
+ drawAll();
168
+ drawRedCross(item);
169
+ setTimeout(drawAll, 500);
170
+ }
171
+ return;
172
+ }
173
+ }
174
+ });
175
+
176
+ startBtn.addEventListener('click', initGame);
177
+ })();
style.css CHANGED
@@ -1,28 +1,75 @@
 
 
 
 
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
 
 
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
 
 
 
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
 
 
 
 
 
 
 
 
 
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
2
+
3
+ * {
4
+ box-sizing: border-box;
5
+ }
6
+
7
  body {
8
+ margin: 0;
9
+ padding: 0;
10
+ background: linear-gradient(135deg, #ece9e6, #ffffff);
11
+ font-family: 'Roboto', 'Segoe UI', Tahoma, sans-serif;
12
+ color: #333;
13
+ display: flex;
14
+ flex-direction: column;
15
+ align-items: center;
16
+ justify-content: flex-start;
17
+ min-height: 100vh;
18
  }
19
 
20
+ .container {
21
+ width: 90%;
22
+ max-width: 900px;
23
+ margin-top: 40px;
24
+ text-align: center;
25
  }
26
 
27
+ #gameInfo {
28
+ font-size: 1.5rem;
29
+ margin-bottom: 1rem;
30
+ background: #fff;
31
+ padding: 0.75rem 1rem;
32
+ border-radius: 8px;
33
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
34
+ display: inline-block;
35
  }
36
 
37
+ #startBtn {
38
+ background: #4CAF50;
39
+ color: #fff;
40
+ border: none;
41
+ border-radius: 4px;
42
+ padding: 0.75rem 1.5rem;
43
+ font-size: 1rem;
44
+ display: block;
45
+ margin: 0 auto 1rem;
46
+ cursor: pointer;
47
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
48
+ transition: background 0.3s ease;
49
+ }
50
+ #startBtn:hover {
51
+ background: #45a049;
52
  }
53
 
54
+ canvas {
55
+ background: #fff;
56
+ border-radius: 8px;
57
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
58
+ margin: 0 auto;
59
+ display: block;
60
+ }
61
+
62
+ #message {
63
+ margin-top: 1rem;
64
+ font-size: 1.25rem;
65
+ min-height: 1.5em;
66
+ }
67
+ #message.correct {
68
+ color: #28a745;
69
+ }
70
+ #message.wrong {
71
+ color: #dc3545;
72
+ }
73
+ #message.clear {
74
+ color: #17a2b8;
75
  }