Skip to content

Commit 6f5a288

Browse files
committed
ㅋㅋ
1 parent 0033590 commit 6f5a288

File tree

2 files changed

+297
-8
lines changed

2 files changed

+297
-8
lines changed

src/GameLoop.cpp

Lines changed: 250 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,250 @@
1-
//
2-
// Created by 김서윤 on 25. 5. 30.
3-
//
1+
#include "GameLoop.hpp"
2+
#include "GameLogic.hpp" // isKingInCheck, findKing, isCheckmate, getPossibleMoves 사용
3+
4+
#include <iostream> // for std::cerr, std::cout
5+
#include <iomanip> // For std::setfill, std::setw
6+
#include <sstream> // For std::ostringstream
7+
8+
// formatTime 함수 정의
9+
std::string formatTime(sf::Time time) {
10+
int totalSeconds = static_cast<int>(time.asSeconds());
11+
if (totalSeconds < 0) totalSeconds = 0;
12+
int minutes = totalSeconds / 60;
13+
int seconds = totalSeconds % 60;
14+
std::ostringstream oss;
15+
oss << std::setfill('0') << std::setw(2) << minutes << ":" << std::setfill('0') << std::setw(2) << seconds;
16+
return oss.str();
17+
}
18+
19+
// 게임 루프 함수 정의
20+
void gameLoop(
21+
sf::RenderWindow& window,
22+
sf::Font& font,
23+
sf::RectangleShape& tile,
24+
sf::Color& lightColor,
25+
sf::Color& darkColor,
26+
sf::Color& checkedKingTileColor,
27+
sf::Text& chooseSidePromptText,
28+
sf::Text& messageText,
29+
sf::Text& whiteTimerText,
30+
sf::Text& blackTimerText,
31+
sf::RectangleShape& whiteStartButton,
32+
sf::Text& whiteStartText,
33+
sf::RectangleShape& blackStartButton,
34+
sf::Text& blackStartText,
35+
sf::RectangleShape& popupBackground,
36+
sf::Text& popupMessageText,
37+
sf::RectangleShape& homeButtonShape,
38+
sf::Text& homeButtonText,
39+
GameState& currentGameState,
40+
std::optional<sf::Vector2i>& selectedPiecePos,
41+
std::vector<sf::Vector2i>& possibleMoves,
42+
PieceColor& currentTurn,
43+
std::string& gameMessageStr,
44+
std::map<std::string, sf::Texture>& textures, // place_piece 람다 때문에 main에서 textures를 캡처하고,
45+
// actualResetGame이 textures를 사용하므로 gameLoop에는 직접 필요 없을 수 있으나,
46+
// 혹시 모를 확장성을 위해 전달 (현재는 actualResetGame이 main의 textures를 캡처)
47+
std::array<std::array<std::optional<Piece>, 8>, 8>& board_state,
48+
sf::Time& whiteTimeLeft,
49+
sf::Time& blackTimeLeft,
50+
sf::Clock& frameClock,
51+
std::function<void()> actualResetGame,
52+
float timerPadding
53+
) {
54+
// --- 메인 게임 루프 ---
55+
while (window.isOpen()) {
56+
sf::Time deltaTime = frameClock.restart();
57+
bool kingIsCurrentlyChecked = false;
58+
sf::Vector2i checkedKingCurrentPos = {-1, -1};
59+
60+
// 게임 상태 업데이트 (시간, 체크/메이트, 메시지)
61+
if (currentGameState == GameState::Playing && currentTurn != PieceColor::None) {
62+
if (currentTurn == PieceColor::White) {
63+
if (whiteTimeLeft > sf::Time::Zero) whiteTimeLeft -= deltaTime;
64+
if (whiteTimeLeft <= sf::Time::Zero) { whiteTimeLeft = sf::Time::Zero; currentGameState = GameState::GameOver; gameMessageStr = "Black wins on time!"; }
65+
} else if (currentTurn == PieceColor::Black) {
66+
if (blackTimeLeft > sf::Time::Zero) blackTimeLeft -= deltaTime;
67+
if (blackTimeLeft <= sf::Time::Zero) { blackTimeLeft = sf::Time::Zero; currentGameState = GameState::GameOver; gameMessageStr = "White wins on time!"; }
68+
}
69+
if (currentGameState != GameState::GameOver) {
70+
kingIsCurrentlyChecked = isKingInCheck(board_state, currentTurn);
71+
if (kingIsCurrentlyChecked) {
72+
checkedKingCurrentPos = findKing(board_state, currentTurn);
73+
if (isCheckmate(board_state, currentTurn)) {
74+
currentGameState = GameState::GameOver; gameMessageStr = std::string((currentTurn == PieceColor::White) ? "Black" : "White") + " wins by Checkmate!";
75+
} else { gameMessageStr = std::string((currentTurn == PieceColor::White) ? "White" : "Black") + " King is in Check!"; }
76+
} else { gameMessageStr = std::string((currentTurn == PieceColor::White) ? "White" : "Black") + " to move"; }
77+
}
78+
} else if (currentGameState == GameState::ChoosingPlayer) {
79+
gameMessageStr = "";
80+
}
81+
82+
// 타이머 텍스트 업데이트
83+
whiteTimerText.setString("White: " + formatTime(whiteTimeLeft));
84+
blackTimerText.setString("Black: " + formatTime(blackTimeLeft));
85+
86+
sf::FloatRect wt_bounds_loop = whiteTimerText.getLocalBounds();
87+
whiteTimerText.setPosition({ BOARD_WIDTH + (BUTTON_PANEL_WIDTH - wt_bounds_loop.size.x) / 2.f - wt_bounds_loop.position.x, timerPadding - wt_bounds_loop.position.y});
88+
sf::FloatRect bt_bounds_loop = blackTimerText.getLocalBounds();
89+
blackTimerText.setPosition({ BOARD_WIDTH + (BUTTON_PANEL_WIDTH - bt_bounds_loop.size.x) / 2.f - bt_bounds_loop.position.x, whiteTimerText.getPosition().y + wt_bounds_loop.size.y + wt_bounds_loop.position.y + 5.f - bt_bounds_loop.position.y });
90+
91+
// 이벤트 처리
92+
while (const auto event_opt = window.pollEvent()) {
93+
const sf::Event& event = *event_opt;
94+
if (event.is<sf::Event::Closed>()) window.close();
95+
else if (const auto* keyPressed = event.getIf<sf::Event::KeyPressed>()) {
96+
if (keyPressed->scancode == sf::Keyboard::Scancode::Escape) window.close();
97+
} else if (const auto* mouseButtonPressed = event.getIf<sf::Event::MouseButtonPressed>()) {
98+
if (mouseButtonPressed->button == sf::Mouse::Button::Left) {
99+
sf::Vector2i mousePos = mouseButtonPressed->position;
100+
101+
if (currentGameState == GameState::ChoosingPlayer) {
102+
if (whiteStartButton.getGlobalBounds().contains(static_cast<sf::Vector2f>(mousePos))) {
103+
currentTurn = PieceColor::White; currentGameState = GameState::Playing;
104+
gameMessageStr = "White to move"; frameClock.restart();
105+
} else if (blackStartButton.getGlobalBounds().contains(static_cast<sf::Vector2f>(mousePos))) {
106+
currentTurn = PieceColor::Black; currentGameState = GameState::Playing;
107+
gameMessageStr = "Black to move"; frameClock.restart();
108+
}
109+
} else if (currentGameState == GameState::Playing) {
110+
int clickedCol = mousePos.x / TILE_SIZE; int clickedRow = mousePos.y / TILE_SIZE;
111+
if (clickedCol >=0 && clickedCol < 8 && clickedRow >=0 && clickedRow < 8) {
112+
bool moved = false;
113+
int fromR_local = -1, fromC_local = -1;
114+
115+
if (selectedPiecePos.has_value()) {
116+
fromR_local = selectedPiecePos->y;
117+
fromC_local = selectedPiecePos->x;
118+
for (const auto& move_coord : possibleMoves) {
119+
if (move_coord.x == clickedCol && move_coord.y == clickedRow) {
120+
std::array<std::array<std::optional<Piece>, 8>, 8> tempBoard = board_state;
121+
std::optional<Piece> pieceToMoveOpt = tempBoard[fromR_local][fromC_local];
122+
if (pieceToMoveOpt.has_value()){
123+
tempBoard[clickedRow][clickedCol] = Piece(pieceToMoveOpt->type, pieceToMoveOpt->color, pieceToMoveOpt->sprite);
124+
tempBoard[fromR_local][fromC_local].reset();
125+
}
126+
if (!isKingInCheck(tempBoard, currentTurn)) {
127+
std::optional<Piece> actualPieceToMoveOpt = board_state[fromR_local][fromC_local];
128+
if(actualPieceToMoveOpt.has_value()){
129+
board_state[clickedRow][clickedCol] = Piece(actualPieceToMoveOpt->type, actualPieceToMoveOpt->color, actualPieceToMoveOpt->sprite);
130+
board_state[fromR_local][fromC_local].reset();
131+
}
132+
if (board_state[clickedRow][clickedCol].has_value()) {
133+
sf::Sprite& movedSprite = board_state[clickedRow][clickedCol]->sprite;
134+
sf::FloatRect spriteBounds = movedSprite.getGlobalBounds();
135+
float x_offset = (static_cast<float>(TILE_SIZE) - spriteBounds.size.x) / 2.f;
136+
float y_offset = (static_cast<float>(TILE_SIZE) - spriteBounds.size.y) / 2.f;
137+
movedSprite.setPosition({clickedCol*static_cast<float>(TILE_SIZE)+x_offset, clickedRow*static_cast<float>(TILE_SIZE)+y_offset});
138+
}
139+
moved = true;
140+
currentTurn = (currentTurn == PieceColor::White) ? PieceColor::Black : PieceColor::White;
141+
frameClock.restart();
142+
break;
143+
} else gameMessageStr = "Invalid move: King would be in check!";
144+
}
145+
}
146+
}
147+
if (moved) {
148+
selectedPiecePos.reset(); possibleMoves.clear();
149+
} else {
150+
if (board_state[clickedRow][clickedCol].has_value() && board_state[clickedRow][clickedCol]->color == currentTurn) {
151+
if (selectedPiecePos.has_value() && selectedPiecePos->x == clickedCol && selectedPiecePos->y == clickedRow) {
152+
selectedPiecePos.reset(); possibleMoves.clear();
153+
} else {
154+
selectedPiecePos = sf::Vector2i(clickedCol, clickedRow);
155+
possibleMoves = getPossibleMoves(board_state, clickedRow, clickedCol);
156+
157+
std::vector<sf::Vector2i> valid_moves;
158+
for(const auto& p_move : possibleMoves){
159+
std::array<std::array<std::optional<Piece>, 8>, 8> temp_board_check = board_state;
160+
std::optional<Piece> piece_to_sim = temp_board_check[clickedRow][clickedCol];
161+
if(piece_to_sim.has_value()){
162+
temp_board_check[p_move.y][p_move.x] = Piece(piece_to_sim->type, piece_to_sim->color, piece_to_sim->sprite);
163+
temp_board_check[clickedRow][clickedCol].reset();
164+
if(!isKingInCheck(temp_board_check, currentTurn)){
165+
valid_moves.push_back(p_move);
166+
}
167+
}
168+
}
169+
possibleMoves = valid_moves;
170+
}
171+
} else {
172+
selectedPiecePos.reset(); possibleMoves.clear();
173+
}
174+
}
175+
} else { selectedPiecePos.reset(); possibleMoves.clear(); }
176+
} else if (currentGameState == GameState::GameOver) {
177+
if (homeButtonShape.getGlobalBounds().contains(static_cast<sf::Vector2f>(mousePos))) {
178+
actualResetGame();
179+
}
180+
}
181+
}
182+
}
183+
}
184+
185+
// 그리기
186+
window.clear(sf::Color(240,240,240));
187+
188+
if (currentGameState == GameState::ChoosingPlayer) {
189+
window.draw(chooseSidePromptText);
190+
window.draw(whiteStartButton); window.draw(whiteStartText);
191+
window.draw(blackStartButton); window.draw(blackStartText);
192+
} else { // Playing or GameOver
193+
for (int r = 0; r < 8; ++r) for (int c = 0; c < 8; ++c) {
194+
bool isLight = (r + c) % 2 == 0;
195+
tile.setFillColor(isLight ? lightColor : darkColor);
196+
if (kingIsCurrentlyChecked && checkedKingCurrentPos.x == c && checkedKingCurrentPos.y == r) tile.setFillColor(checkedKingTileColor);
197+
else if (selectedPiecePos.has_value() && selectedPiecePos->x == c && selectedPiecePos->y == r) tile.setFillColor(sf::Color(255, 255, 0, 150));
198+
else {
199+
for (const auto& move_coord : possibleMoves) {
200+
if (move_coord.x == c && move_coord.y == r) {
201+
if (board_state[r][c].has_value() && board_state[r][c]->color != currentTurn) tile.setFillColor(sf::Color(50, 150, 250, 150));
202+
else tile.setFillColor(sf::Color(100, 250, 50, 150));
203+
break;
204+
}
205+
}
206+
}
207+
tile.setPosition({c * static_cast<float>(TILE_SIZE), r * static_cast<float>(TILE_SIZE)});
208+
window.draw(tile);
209+
}
210+
for (int r_idx = 0; r_idx < 8; ++r_idx) for (int c_idx = 0; c_idx < 8; ++c_idx) {
211+
if (board_state[r_idx][c_idx].has_value()) {
212+
// SFML 렌더링 시 const가 아닌 스프라이트가 필요할 수 있으므로,
213+
// board_state에서 Piece를 가져올 때 value()로 복사본을 사용하거나,
214+
// GameLogic에 const가 아닌 접근자를 만들거나,
215+
// UIManager가 board_state의 복사본을 가지도록 설계 변경 필요.
216+
// 여기서는 가장 간단하게 복사본을 사용합니다.
217+
Piece piece_to_draw = board_state[r_idx][c_idx].value();
218+
bool isLosingKing = (currentGameState == GameState::GameOver && piece_to_draw.type == PieceType::King && piece_to_draw.color == currentTurn);
219+
if (isLosingKing) piece_to_draw.sprite.setColor(sf::Color::Red);
220+
else piece_to_draw.sprite.setColor(sf::Color::White); // 항상 기본 색상으로 리셋
221+
window.draw(piece_to_draw.sprite);
222+
}
223+
}
224+
window.draw(whiteTimerText); window.draw(blackTimerText);
225+
if (!gameMessageStr.empty()) {
226+
messageText.setString(gameMessageStr);
227+
sf::FloatRect gameMsgLocalBounds = messageText.getLocalBounds();
228+
messageText.setPosition({ BOARD_WIDTH + (BUTTON_PANEL_WIDTH - gameMsgLocalBounds.size.x) / 2.f - gameMsgLocalBounds.position.x, (WINDOW_HEIGHT - gameMsgLocalBounds.size.y) / 2.f - gameMsgLocalBounds.position.y });
229+
messageText.setFillColor(sf::Color::Black); messageText.setStyle(sf::Text::Regular);
230+
if(currentGameState == GameState::GameOver || gameMessageStr.find("wins by") != std::string::npos ) { messageText.setFillColor(sf::Color::Red); messageText.setStyle(sf::Text::Bold); }
231+
else if (gameMessageStr.find("Check!") != std::string::npos ) { messageText.setFillColor(sf::Color(200,0,0)); messageText.setStyle(sf::Text::Bold); }
232+
window.draw(messageText);
233+
}
234+
235+
if (currentGameState == GameState::GameOver) {
236+
window.draw(popupBackground);
237+
popupMessageText.setString(gameMessageStr);
238+
sf::FloatRect popupMsgBounds = popupMessageText.getLocalBounds();
239+
popupMessageText.setPosition({ popupBackground.getPosition().x - popupMsgBounds.size.x / 2.f - popupMsgBounds.position.x, popupBackground.getPosition().y - popupBackground.getSize().y / 2.f + 30.f - popupMsgBounds.position.y });
240+
window.draw(popupMessageText);
241+
homeButtonShape.setPosition({ popupBackground.getPosition().x, popupBackground.getPosition().y + popupBackground.getSize().y / 2.f - 40.f - homeButtonShape.getSize().y / 2.f });
242+
window.draw(homeButtonShape);
243+
sf::FloatRect homeButtonTextBounds = homeButtonText.getLocalBounds(); // popupMessageText의 바운드를 사용하던 것을 수정
244+
homeButtonText.setPosition({ homeButtonShape.getPosition().x - homeButtonTextBounds.size.x / 2.f - homeButtonTextBounds.position.x, homeButtonShape.getPosition().y - homeButtonTextBounds.size.y / 2.f - homeButtonTextBounds.position.y });
245+
window.draw(homeButtonText);
246+
}
247+
}
248+
window.display();
249+
}
250+
}

src/GameLoop.hpp

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,50 @@
1-
//
2-
// Created by 김서윤 on 25. 5. 30.
3-
//
4-
51
#ifndef GAMELOOP_HPP
62
#define GAMELOOP_HPP
73

8-
#endif //GAMELOOP_HPP
4+
#include "GameData.hpp" // GameState, PieceColor, Piece 등 사용
5+
#include <SFML/Graphics.hpp>
6+
#include <string>
7+
#include <vector>
8+
#include <array>
9+
#include <optional>
10+
#include <map>
11+
#include <functional> // For std::function
12+
13+
// formatTime 함수 선언
14+
std::string formatTime(sf::Time time);
15+
16+
// 게임 루프 함수 선언
17+
void gameLoop(
18+
sf::RenderWindow& window,
19+
sf::Font& font,
20+
sf::RectangleShape& tile,
21+
sf::Color& lightColor,
22+
sf::Color& darkColor,
23+
sf::Color& checkedKingTileColor,
24+
sf::Text& chooseSidePromptText,
25+
sf::Text& messageText,
26+
sf::Text& whiteTimerText,
27+
sf::Text& blackTimerText,
28+
sf::RectangleShape& whiteStartButton,
29+
sf::Text& whiteStartText,
30+
sf::RectangleShape& blackStartButton,
31+
sf::Text& blackStartText,
32+
sf::RectangleShape& popupBackground,
33+
sf::Text& popupMessageText,
34+
sf::RectangleShape& homeButtonShape,
35+
sf::Text& homeButtonText,
36+
GameState& currentGameState,
37+
std::optional<sf::Vector2i>& selectedPiecePos,
38+
std::vector<sf::Vector2i>& possibleMoves,
39+
PieceColor& currentTurn,
40+
std::string& gameMessageStr,
41+
std::map<std::string, sf::Texture>& textures,
42+
std::array<std::array<std::optional<Piece>, 8>, 8>& board_state,
43+
sf::Time& whiteTimeLeft,
44+
sf::Time& blackTimeLeft,
45+
sf::Clock& frameClock,
46+
std::function<void()> actualResetGame,
47+
float timerPadding
48+
);
49+
50+
#endif // GAMELOOP_HPP

0 commit comments

Comments
 (0)