Pawe艂 艁aba commited on
Commit
f77b5cc
1 Parent(s): 8345cd8

zmiana w uruchamianiu proagramu

Browse files
Files changed (3) hide show
  1. public/index.html +1 -0
  2. public/script.js +41 -10
  3. train1.py +244 -0
public/index.html CHANGED
@@ -11,6 +11,7 @@
11
  <h1>K贸艂ko i Krzy偶yk</h1>
12
  <div class="board"></div>
13
  <div class="message"></div>
 
14
  </div>
15
 
16
 
 
11
  <h1>K贸艂ko i Krzy偶yk</h1>
12
  <div class="board"></div>
13
  <div class="message"></div>
14
+ <button class="restart" onclick="restart()">Restart</button>
15
  </div>
16
 
17
 
public/script.js CHANGED
@@ -1,10 +1,18 @@
1
  const board = Array(9).fill(0);
 
 
2
  const winPatterns = [
3
  [0, 1, 2], [3, 4, 5], [6, 7, 8], // poziome
4
  [0, 3, 6], [1, 4, 7], [2, 5, 8], // pionowe
5
  [0, 4, 8], [2, 4, 6] // przek膮tne
6
  ];
7
 
 
 
 
 
 
 
8
  function checkWin(board) {
9
  for (let pattern of winPatterns) {
10
  const [a, b, c] = pattern;
@@ -26,7 +34,9 @@ function initializeBoard() {
26
  div.addEventListener('click', () => handleMove(i));
27
  boardElement.appendChild(div);
28
  }
29
-
 
 
30
  updateBoardDisplay();
31
  }
32
 
@@ -47,6 +57,21 @@ function updateBoardDisplay() {
47
  messageElement.textContent = 'Komputer wygra艂!';
48
  } else if (winner === -1) {
49
  messageElement.textContent = 'Wygra艂e艣!';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  } else if (!board.includes(0)) {
51
  messageElement.textContent = 'Remis!';
52
  } else {
@@ -54,15 +79,7 @@ function updateBoardDisplay() {
54
  }
55
  }
56
 
57
- async function handleMove(index) {
58
- if (board[index] !== 0 || checkWin(board)) return;
59
-
60
- // Ruch gracza
61
- board[index] = -1;
62
- updateBoardDisplay();
63
-
64
- if (checkWin(board)) return;
65
-
66
  try {
67
  // Wys艂anie stanu planszy do API
68
  const response = await fetch('/move', {
@@ -85,6 +102,20 @@ async function handleMove(index) {
85
  }
86
  }
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  // Inicjalizacja gry i pobranie aktualnego stanu
89
  async function initGame() {
90
  try {
 
1
  const board = Array(9).fill(0);
2
+ const step = [];
3
+ let fitstMove = true;
4
  const winPatterns = [
5
  [0, 1, 2], [3, 4, 5], [6, 7, 8], // poziome
6
  [0, 3, 6], [1, 4, 7], [2, 5, 8], // pionowe
7
  [0, 4, 8], [2, 4, 6] // przek膮tne
8
  ];
9
 
10
+ function restart(){
11
+ board = Array(9).fill(0);
12
+ step = [];
13
+ fitstMove = !fitstMove;
14
+ }
15
+
16
  function checkWin(board) {
17
  for (let pattern of winPatterns) {
18
  const [a, b, c] = pattern;
 
34
  div.addEventListener('click', () => handleMove(i));
35
  boardElement.appendChild(div);
36
  }
37
+ if(!fitstMove){
38
+ apiStep();
39
+ }
40
  updateBoardDisplay();
41
  }
42
 
 
57
  messageElement.textContent = 'Komputer wygra艂!';
58
  } else if (winner === -1) {
59
  messageElement.textContent = 'Wygra艂e艣!';
60
+ let newBoard = []
61
+ for (let i = 0; i < 9 - 1; i++) {
62
+ let value = 0;
63
+ if(board[i]!=0)
64
+ value = board[i]==1?-1:1;
65
+ newBoard.push(value);
66
+ fetch('/savegame', {
67
+ method: 'POST',
68
+ headers: {
69
+ 'Content-Type': 'application/json'
70
+ },
71
+ body: JSON.stringify({ board: newBoard })
72
+ });
73
+ }
74
+
75
  } else if (!board.includes(0)) {
76
  messageElement.textContent = 'Remis!';
77
  } else {
 
79
  }
80
  }
81
 
82
+ async function apiStep() {
 
 
 
 
 
 
 
 
83
  try {
84
  // Wys艂anie stanu planszy do API
85
  const response = await fetch('/move', {
 
102
  }
103
  }
104
 
105
+ async function handleMove(index) {
106
+ if (board[index] !== 0 || checkWin(board)) return;
107
+
108
+ // Ruch gracza
109
+ board[index] = -1;
110
+ updateBoardDisplay();
111
+ // dodaj gkrok do historii
112
+ step.push(index);
113
+
114
+ if (checkWin(board)) return;
115
+
116
+ apiStep();
117
+ }
118
+
119
  // Inicjalizacja gry i pobranie aktualnego stanu
120
  async function initGame() {
121
  try {
train1.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from tensorflow import keras
3
+ from tensorflow.keras import layers
4
+ import json
5
+ import random
6
+ import argparse
7
+ import os
8
+ import hashlib
9
+ from pathlib import Path
10
+
11
+ class TicTacToeTrainer:
12
+ def __init__(self):
13
+ self.model = None
14
+
15
+ def create_model(self):
16
+ """Tworzy model sieci neuronowej"""
17
+ model = keras.Sequential([
18
+ layers.Dense(128, activation='relu', input_shape=(9,)),
19
+ layers.Dropout(0.3),
20
+ layers.Dense(64, activation='relu'),
21
+ layers.Dropout(0.3),
22
+ layers.Dense(9, activation='softmax')
23
+ ])
24
+
25
+ model.compile(
26
+ optimizer='adam',
27
+ loss='categorical_crossentropy',
28
+ metrics=['accuracy']
29
+ )
30
+ return model
31
+
32
+ def calculate_board_hash(self, board):
33
+ """Calculate a unique hash for the board state"""
34
+ return hashlib.md5(str(board.tolist()).encode()).hexdigest()
35
+
36
+ def check_two_in_line(self, board, player):
37
+ """
38
+ Check if player has two in a line and return the winning move position
39
+ Returns: Position to block or None if no blocking needed
40
+ """
41
+ winning_combinations = [
42
+ [0, 1, 2], [3, 4, 5], [6, 7, 8], # Horizontal
43
+ [0, 3, 6], [1, 4, 7], [2, 5, 8], # Vertical
44
+ [0, 4, 8], [2, 4, 6] # Diagonal
45
+ ]
46
+
47
+ for combo in winning_combinations:
48
+ line = board[combo]
49
+ if sum(line == player) == 2 and sum(line == 0) == 1:
50
+ # Return the empty position in the line
51
+ return combo[list(line).index(0)]
52
+ return None
53
+
54
+ def check_winner(self, board):
55
+ """
56
+ Sprawdza czy jest zwyci臋zca lub czy mamy dwa znaki w linii
57
+ Returns: (bool, str) - (czy wygrana/potencjalna wygrana, typ sytuacji)
58
+ """
59
+ winning_combinations = [
60
+ [0, 1, 2], [3, 4, 5], [6, 7, 8], # Horizontal
61
+ [0, 3, 6], [1, 4, 7], [2, 5, 8], # Vertical
62
+ [0, 4, 8], [2, 4, 6] # Diagonal
63
+ ]
64
+
65
+ # Sprawd藕 pe艂n膮 wygran膮 (3 w linii)
66
+ for combo in winning_combinations:
67
+ if sum(board[combo]) == 3:
68
+ return True, "win"
69
+
70
+ # Sprawd藕 czy mamy dwa w linii z pustym polem
71
+ for combo in winning_combinations:
72
+ line = board[combo]
73
+ if sum(line == 1) == 2 and sum(line == 0) == 1:
74
+ return True, "two_in_line"
75
+
76
+ return False, "none"
77
+
78
+ def generate_training_data(self, num_games=1000):
79
+ """
80
+ Generates unique training data including two-in-line positions
81
+ """
82
+ X = []
83
+ y = []
84
+ games_hash_set = set()
85
+
86
+ # Load existing games if file exists
87
+ json_file = Path('games_data.json')
88
+ if json_file.exists():
89
+ try:
90
+ with open(json_file, 'r') as file:
91
+ existing_games = json.load(file)
92
+ for game in existing_games:
93
+ games_hash_set.add(game['hash'])
94
+ print(f"Loaded {len(existing_games)} existing games")
95
+ except json.JSONDecodeError:
96
+ print("Error reading JSON file. Starting with empty games list.")
97
+ existing_games = []
98
+ else:
99
+ existing_games = []
100
+
101
+ new_games = []
102
+ games_generated = 0
103
+ attempts = 0
104
+ max_attempts = num_games * 10
105
+
106
+ while games_generated < num_games and attempts < max_attempts:
107
+ attempts += 1
108
+ board = np.zeros((9,), dtype=int)
109
+ game_states = []
110
+ game_moves = []
111
+ full_sequence = []
112
+
113
+ while True:
114
+ current_state = board.copy()
115
+ valid_moves = np.where(board == 0)[0]
116
+
117
+ if len(valid_moves) == 0:
118
+ break
119
+
120
+ # Player 1 move
121
+ move = random.choice(valid_moves)
122
+ move_one_hot = np.zeros(9)
123
+ move_one_hot[move] = 1
124
+
125
+ game_states.append(current_state.copy())
126
+ game_moves.append(move_one_hot)
127
+ full_sequence.append({'player': 'X', 'move': int(move)})
128
+
129
+ board[move] = 1
130
+
131
+ # Sprawd藕 wygran膮 lub dwa w linii
132
+ is_winning, situation = self.check_winner(board)
133
+ if is_winning or len(np.where(board == 0)[0]) == 0:
134
+ break
135
+
136
+ # Player 2 move (defensive)
137
+ valid_moves = np.where(board == 0)[0]
138
+ if len(valid_moves) > 0:
139
+ blocking_move = self.check_two_in_line(board, 1)
140
+ if blocking_move is not None and board[blocking_move] == 0:
141
+ opponent_move = blocking_move
142
+ else:
143
+ opponent_move = random.choice(valid_moves)
144
+ board[opponent_move] = -1
145
+ full_sequence.append({'player': 'O', 'move': int(opponent_move)})
146
+
147
+ # Calculate hash for the game
148
+ game_hash = self.calculate_board_hash(board)
149
+
150
+ # If game is unique and ended in a win or two-in-line
151
+ is_winning, situation = self.check_winner(board)
152
+ if game_hash not in games_hash_set and is_winning:
153
+ games_hash_set.add(game_hash)
154
+ games_generated += 1
155
+
156
+ game_data = {
157
+ 'hash': game_hash,
158
+ 'moves': full_sequence,
159
+ 'final_board': board.tolist(),
160
+ 'win': situation == "win",
161
+ 'situation': situation
162
+ }
163
+ new_games.append(game_data)
164
+
165
+ X.extend(game_states)
166
+ y.extend(game_moves)
167
+
168
+ if games_generated % 10 == 0:
169
+ print(f"Generated {games_generated}/{num_games} unique games")
170
+ print(f"Last game situation: {situation}")
171
+
172
+ all_games = existing_games + new_games
173
+
174
+ with open(json_file, 'w') as file:
175
+ json.dump(all_games, file, indent=2)
176
+
177
+ print(f"\nGenerated {len(new_games)} new unique games")
178
+ print(f"Total games in database: {len(all_games)}")
179
+
180
+ return np.array(X), np.array(y)
181
+
182
+
183
+
184
+ def train(self, epochs=50, games=1000, model_path='model'):
185
+ """Trenuje model i zapisuje go do pliku"""
186
+ print(f"Rozpoczynam generowanie danych treningowych ({games} gier)...")
187
+ X_train, y_train = self.generate_training_data(games)
188
+
189
+ if len(X_train) == 0:
190
+ print("Nie uda艂o si臋 wygenerowa膰 偶adnych danych treningowych!")
191
+ return
192
+
193
+ print(f"\nWygenerowano dane treningowe: {len(X_train)} przyk艂ad贸w")
194
+ print(f"Przyk艂adowy stan planszy: {X_train[0]}")
195
+
196
+ print(f"\nRozpoczynam trening ({epochs} epok)...")
197
+ self.model = self.create_model()
198
+
199
+ history = self.model.fit(
200
+ X_train,
201
+ y_train,
202
+ epochs=epochs,
203
+ batch_size=32,
204
+ validation_split=0.1,
205
+ verbose=1
206
+ )
207
+
208
+ # Tworzenie katalogu je艣li nie istnieje
209
+ os.makedirs(model_path, exist_ok=True)
210
+
211
+ # Zapisywanie modelu
212
+ self.model.save(model_path + "/model.keras")
213
+ print(f"\nModel zosta艂 zapisany w: {model_path}")
214
+
215
+ # Zapisywanie metryk treningu
216
+ metrics = {
217
+ 'accuracy': float(history.history['accuracy'][-1]),
218
+ 'val_accuracy': float(history.history['val_accuracy'][-1]),
219
+ 'loss': float(history.history['loss'][-1]),
220
+ 'val_loss': float(history.history['val_loss'][-1])
221
+ }
222
+
223
+ print("\nWyniki treningu:")
224
+ print(f"Dok艂adno艣膰: {metrics['accuracy']:.4f}")
225
+ print(f"Dok艂adno艣膰 walidacji: {metrics['val_accuracy']:.4f}")
226
+ print(f"Strata: {metrics['loss']:.4f}")
227
+ print(f"Strata walidacji: {metrics['val_loss']:.4f}")
228
+
229
+ def main():
230
+ parser = argparse.ArgumentParser(description='Trenuj model AI do gry w k贸艂ko i krzy偶yk')
231
+ parser.add_argument('--epochs', type=int, default=50,
232
+ help='Liczba epok treningu (domy艣lnie: 50)')
233
+ parser.add_argument('--games', type=int, default=1000,
234
+ help='Liczba gier treningowych (domy艣lnie: 1000)')
235
+ parser.add_argument('--model-path', type=str, default='model',
236
+ help='艢cie偶ka do zapisania modelu (domy艣lnie: "model")')
237
+
238
+ args = parser.parse_args()
239
+
240
+ trainer = TicTacToeTrainer()
241
+ trainer.train(epochs=args.epochs, games=args.games, model_path=args.model_path)
242
+
243
+ if __name__ == "__main__":
244
+ main()