From 0937fc35deb65b6b4cfb8b0b2e15a2c9fac7a00f Mon Sep 17 00:00:00 2001 From: Hymmel Date: Tue, 10 Feb 2026 14:08:30 +0100 Subject: [PATCH] gay --- README.md | 7 ++ .../java/com/lona/tictactoe/client/Main.java | 54 +++++++++++-- .../com/lona/tictactoe/client/Main.class | Bin 10537 -> 11580 bytes client/target/client-1.0-SNAPSHOT.jar | Bin 8164 -> 8724 bytes client/target/maven-archiver/pom.properties | 2 +- .../target/original-client-1.0-SNAPSHOT.jar | Bin 7920 -> 8480 bytes .../lona/tictactoe/server/ClientHandler.java | 28 ++++++- .../lona/tictactoe/server/GameInstance.java | 49 ++++++++---- .../lona/tictactoe/server/ClientHandler.class | Bin 4539 -> 4903 bytes .../lona/tictactoe/server/GameInstance.class | Bin 3909 -> 4238 bytes server/target/maven-archiver/pom.properties | 2 +- .../target/original-server-1.0-SNAPSHOT.jar | Bin 9249 -> 9650 bytes server/target/server-1.0-SNAPSHOT.jar | Bin 9547 -> 9948 bytes .../src/main/resources/static/css/style.css | 31 +++++--- .../src/main/resources/static/js/app.js | 74 +++++++----------- .../src/main/resources/templates/index.html | 16 ++-- .../target/classes/static/css/style.css | 31 +++++--- web-client/target/classes/static/js/app.js | 74 +++++++----------- .../target/classes/templates/index.html | 16 ++-- web-client/target/web-client-1.0-SNAPSHOT.jar | Bin 22319803 -> 22319653 bytes .../web-client-1.0-SNAPSHOT.jar.original | Bin 10831 -> 10681 bytes 21 files changed, 238 insertions(+), 146 deletions(-) diff --git a/README.md b/README.md index df719e2..a196253 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,13 @@ The server creates game sessions identified by a unique 6-character code. - **JOIN <CODE>**: Joins an existing game. Server returns `JOIN_SUCCESS` or `ERROR`. - **MOVE <ROW> <COL>**: Places your symbol (0-2). Validated by server. - **SURRENDER**: Forfeits the game immediately. +- **LEAVE**: Leaves the current lobby. If empty, the lobby acts as closed. +- **RESTART**: Resets the board for the current lobby so players can play again. + +**Server Messages:** +- `WIN ` or `DRAW` +- `OPPONENT_LEFT`: Sent when the other player leaves via the LEAVE command. +- `RESTART`: Sent when the game is restarted. ## Troubleshooting diff --git a/client/src/main/java/com/lona/tictactoe/client/Main.java b/client/src/main/java/com/lona/tictactoe/client/Main.java index b04f795..d4caaf8 100644 --- a/client/src/main/java/com/lona/tictactoe/client/Main.java +++ b/client/src/main/java/com/lona/tictactoe/client/Main.java @@ -24,7 +24,7 @@ public class Main extends JFrame { private char mySymbol = ' '; // assigned by server implied turn public static void main(String[] args) { - String host = "dokploy.lona-development.org"; + String host = "38.242.130.81"; int port = 1870; if (args.length > 0) { @@ -126,9 +126,26 @@ public class Main extends JFrame { } gamePanel.add(boardPanel, BorderLayout.CENTER); - JButton surrenderBtn = new JButton("Surrender / Give Up"); + // --- BUTTONS PANEL --- + JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 10, 0)); + + JButton leaveBtn = new JButton("Leave Lobby"); + leaveBtn.setBackground(Color.GRAY); + leaveBtn.setForeground(Color.WHITE); + leaveBtn.addActionListener(e -> { + send("LEAVE"); + resetGame(); + }); + + JButton surrenderBtn = new JButton("Surrender"); + surrenderBtn.setBackground(new Color(220, 53, 69)); // Bootstrap Danger Red + surrenderBtn.setForeground(Color.WHITE); surrenderBtn.addActionListener(e -> send("SURRENDER")); - gamePanel.add(surrenderBtn, BorderLayout.SOUTH); + + buttonPanel.add(leaveBtn); + buttonPanel.add(surrenderBtn); + + gamePanel.add(buttonPanel, BorderLayout.SOUTH); mainPanel.add(menuPanel, "MENU"); mainPanel.add(gamePanel, "GAME"); @@ -155,6 +172,7 @@ public class Main extends JFrame { SwingUtilities.invokeLater(() -> { statusLabel.setText("Connection Failed."); JOptionPane.showMessageDialog(this, "Connection Error: " + e.getMessage()); + // Don't reset to menu if we can't connect, let user see error }); } }).start(); @@ -191,6 +209,12 @@ public class Main extends JFrame { case "START": statusLabel.setText("Game Started! X goes first."); break; + case "RESTART": + // clear board + for (JButton btn : buttons) + btn.setText(""); + statusLabel.setText("Game Restarted! X goes first."); + break; case "BOARD": // BOARD X O X ... String boardStr = msg.substring(6); // remove "BOARD " for (int i = 0; i < 9 && i < boardStr.length(); i++) { @@ -210,11 +234,29 @@ public class Main extends JFrame { break; case "WIN": String winner = msg.substring(4); - JOptionPane.showMessageDialog(this, "Winner: " + winner); - resetGame(); + statusLabel.setText("Winner: " + winner); + int choice = JOptionPane.showConfirmDialog(this, + "Winner: " + winner + "\nPlay Again?", + "Game Over", + JOptionPane.YES_NO_OPTION); + + if (choice == JOptionPane.YES_OPTION) { + send("RESTART"); + } break; case "DRAW": - JOptionPane.showMessageDialog(this, "Draw!"); + statusLabel.setText("Draw!"); + int drawChoice = JOptionPane.showConfirmDialog(this, + "Draw!\nPlay Again?", + "Game Over", + JOptionPane.YES_NO_OPTION); + + if (drawChoice == JOptionPane.YES_OPTION) { + send("RESTART"); + } + break; + case "OPPONENT_LEFT": + JOptionPane.showMessageDialog(this, "Opponent left the game."); resetGame(); break; case "ERROR:": diff --git a/client/target/classes/com/lona/tictactoe/client/Main.class b/client/target/classes/com/lona/tictactoe/client/Main.class index 3768a7dd139b6c92c53dc3e7d4b9ca4297771cd9..79dff58e5d77d65de24db87070fa675c386c09cc 100644 GIT binary patch delta 5906 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 delta 4762 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 U{~smco3xNZdR3eGqr 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 Y$iEc-m;XP?-+iL_w?`8*fc9DXf2^x+tN;K2 diff --git a/server/src/main/java/com/lona/tictactoe/server/ClientHandler.java b/server/src/main/java/com/lona/tictactoe/server/ClientHandler.java index 5337ffe..5e87886 100644 --- a/server/src/main/java/com/lona/tictactoe/server/ClientHandler.java +++ b/server/src/main/java/com/lona/tictactoe/server/ClientHandler.java @@ -85,6 +85,27 @@ public class ClientHandler implements Runnable { } break; + case "RESTART": + if (gameCode != null) { + GameInstance active = GameManager.getGame(gameCode); + if (active != null) + active.restart(); + } + break; + + case "LEAVE": + if (gameCode != null) { + GameInstance active = GameManager.getGame(gameCode); + if (active != null) { + active.leave(this); + if (active.isEmpty()) { + GameManager.removeGame(gameCode); + } + } + this.gameCode = null; + } + break; + default: out.println("ERROR: Unknown command"); } @@ -94,9 +115,12 @@ public class ClientHandler implements Runnable { } finally { if (gameCode != null) { GameInstance g = GameManager.getGame(gameCode); - if (g != null) + if (g != null) { g.disconnect(this); - GameManager.removeGame(gameCode); + if (g.isEmpty()) { + GameManager.removeGame(gameCode); + } + } } try { client.close(); diff --git a/server/src/main/java/com/lona/tictactoe/server/GameInstance.java b/server/src/main/java/com/lona/tictactoe/server/GameInstance.java index 1a8f1ff..c5b52b7 100644 --- a/server/src/main/java/com/lona/tictactoe/server/GameInstance.java +++ b/server/src/main/java/com/lona/tictactoe/server/GameInstance.java @@ -35,36 +35,30 @@ public class GameInstance { char symbol = (player == playerX) ? 'X' : 'O'; // --- ANTICHEAT / VALIDATION --- - // 1. Check if correct turn if (symbol != currentTurn) { player.sendMessage("ERROR: It is not your turn!"); return; } - // 2. Check bounds if (x < 0 || x > 2 || y < 0 || y > 2) { player.sendMessage("ERROR: Invalid coordinates!"); return; } - // 3. Check if cell is empty if (board[x][y] != ' ') { player.sendMessage("ERROR: Cell occupied!"); return; } - // Apply move board[x][y] = symbol; sendBoard(); if (checkWin(symbol)) { finished = true; broadcast("WIN " + symbol); - createEndState(); } else if (checkDraw()) { finished = true; broadcast("DRAW"); - createEndState(); } else { currentTurn = (currentTurn == 'X') ? 'O' : 'X'; broadcast("TURN " + currentTurn); @@ -78,19 +72,42 @@ public class GameInstance { char loser = (player == playerX) ? 'X' : 'O'; char winner = (loser == 'X') ? 'O' : 'X'; broadcast("WIN " + winner + " (Surrender)"); - createEndState(); + } + + public synchronized void restart() { + for (char[] row : board) + Arrays.fill(row, ' '); + currentTurn = 'X'; + finished = false; + broadcast("RESTART"); + sendBoard(); + broadcast("TURN " + currentTurn); + } + + public synchronized void leave(ClientHandler player) { + if (player == playerX) { + // Creator left, if playerO exists, tell them? + if (playerO != null) { + playerO.sendMessage("OPPONENT_LEFT"); + } + playerX = null; + } else if (player == playerO) { + if (playerX != null) { + playerX.sendMessage("OPPONENT_LEFT"); + } + playerO = null; + } + // If empty, GameManager will eventually remove if we allow it, + // but for now let's rely on GameManager to check if empty logic or just keep it + // simple. + } + + public synchronized boolean isEmpty() { + return playerX == null && playerO == null; } public synchronized void disconnect(ClientHandler player) { - if (finished) - return; - finished = true; - broadcast("ERROR: Opponent disconnected"); - createEndState(); - } - - private void createEndState() { - // cleanup if needed + leave(player); } private boolean checkWin(char s) { diff --git a/server/target/classes/com/lona/tictactoe/server/ClientHandler.class b/server/target/classes/com/lona/tictactoe/server/ClientHandler.class index f59a9d559686b47cb1d8aff7ed20da9b9848d2a8..66d24fbd7259c10e4fd1d7e56ab750edc1a15724 100644 GIT binary patch delta 2427 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 diff --git a/server/target/maven-archiver/pom.properties b/server/target/maven-archiver/pom.properties index f92dd76..2bfbc85 100644 --- a/server/target/maven-archiver/pom.properties +++ b/server/target/maven-archiver/pom.properties @@ -1,5 +1,5 @@ #Generated by Maven -#Tue Feb 10 13:26:46 CET 2026 +#Tue Feb 10 14:07:14 CET 2026 artifactId=server groupId=com.lona.tictactoe version=1.0-SNAPSHOT diff --git a/server/target/original-server-1.0-SNAPSHOT.jar b/server/target/original-server-1.0-SNAPSHOT.jar index 8d1fd2e7495471a7ba9c25ff422f9ff85a3ffe31..6cfe39af1a71f91b0ab898d6b4fcd8de35664238 100644 GIT binary patch delta 5505 zcmZXYcQjmI*T&x<(TV6SdWp{Hy+-eh7DO4*qDC1))J*h}D5FJ?j53H`Lv#^@ zkq|W^+J8jd!M_{I`^;7{UWrQb;%8N@$Zo0+>Tvn7CATG4m3-==r&G) zZ{zW82F`>ld@H)RQg?TtI=0fP_i2bpaB!Ye!833rVBSHrpMlZl)}F56QMCD~!eAoi z`y?Xm9VkKu8>#jFg-6Uw3%JcM=)2nLLoB>$&NY}l;P%-qkV55YZQwMzn4j&Fo+wE6 zn@!b*eg4#;fo>gZi~U|CUflK&-L+UM+@Yc9e2*Biln4jZ)dicDX{dy|O$Cg0$#w2- z0r&!U%w)~W%vH9?qZr&q@AL8u^jm}W8||OM3Yw71f>MV#Ka=(?eNQ`E zmK%C>G~Gt8>PvyX9-6NeU$%3q>=EpsOVqvcAQBB76bwyNK#;eCFSK}B-PJHlb!C)9 z!_Su6Ka1MuIu)g@izuAj4b2#QwXU3ItVo5zsbTv8-_jJRe&w{gJB2{?pr&T zVSMmAk@Zf-r0Q{u?}VUGu%gOq1FHv}EsqpYkzVUl?`y=5F6ccu^O&1#*(JLjxVVbN zNePgzHu6ZVOGs)H&tY^v7l9@;d%!Z1O;Up1q~yBf?wF29m-kj}q5Guc{xKA<5eI4J7@@-$5 zdlXSEWm;!HP~h8&umf$JZtPu@Hf=a*S@oPRhWwycOPt^mh2iBIE}FkC!iI3X=_Ta& zqNevJNp!5ORNTt5Ay6+aSv)N@A4)GCgGputEFu~0aFCeK>`((+E2h^>+Y*@hSNsx} zMQxCL&DD&6=V!43)XK+e)TH*bqLRGMHs+aHZyyytd6V=`|B1aW-M%3@>Yhw9(`lr4 zCgbNYIY8DN3dX~#zDc6WU-p$CvM;Eo*gC!B9R zRf8->u)c9YJ{p4Y)xfjKk2A-tS7}hTZHlx7tlUj=BOk$}SR zi|a3=!txl-8%Ta;Jc#)a*a{BI7_`hUR$i+#a*;<5!Sk9+h8Fm*SVyvu8v=Q zv;1wx4h9J`b2AKtxz`!R{5E$(kY7J(xf5SQ!`}rfJnQn3~)8rNNxqtbr}}qutYw46#8k zzV1l)(6A{g9%fuQVSWMMo~IZyKrqCoMW$i1_c?{O3zbl^j@x6O0Xv0k3GynRcx#6F z>0x`?dz*BC@Pf|BgxbQ&PvbA>{4KJuLZw)%@XXr%mezYs(WxXSjb})}CmX>cm=2BW zU#~r}+4VS1&ilS@6+|mYVFX9KGA3qdr9G7{_%~fVyaqsSm*9@^c>aTx){TU&c9Sm^C?m%cHDI=~bb zy^geKX$0!r7v_!xHXmmOz#bcI96NAZ#96F29EU&UU00_cqRf9zCQszBvv}-A?FbZ3 zIHqxo8Q^Rrg_P?^7{z5Ohvi+jnNx3p*l7Z?e)IVU(i$0oV5p(wg`+}7w!;`dr!1ru zyc0%Izuw93ybdy0ob4{>BukJyF zJ!`$J?+^I$27bmb!)w-_pYSfyz3W%rj9Es@C~IM2?8-@8{<(V3RNY$ORJq#kPk{^# zW1FYA(~`gBBQj2UCRBU+&qvCy;<9xXEcMPcjC)o!Uvn6m4J!x@$#xEvCyC>ci@6af z|$|rxQ0k|5F+<=3SM5ecvhdbm9)&2`+A+qj4@5tUv9U&1)iDJX6bm-duq!%Cw}Tc zCLk71L2k_s$U-hX=lma9A;{?N3g$CH-F8V`4T5{zcK~{WGUVT?1i4_^4Q}l_2R^Gm z8U-O^BBQl$lQTRE9?Wo+k(+I1iUZv&7zTX;Cm4)Hn0Kbb$K(%0Iqmz!nt#hD);7JI z`(461Hf>A-fq1@%e@BjJdY2#Ig&%5Nw1-D<3}x?=ko|ZEr*D+x%pf0pJTZvph*b?h zzs@Z10|priDogh7JQ#wlv<)lRzKNB!?f)1OCozy(`byiQ)&eRjq2gg*22YLJ6JC@|W-dSk24}k8BZ+GDFv>NV4c5{6-(7jT6 zn}wq$DO+k?SldECYdXCn+2(AG}=v zFT)M0`YHkDGaUJZeJ!P~-ZeOCu`h`w^yHSEwf4qB z`4RiboJIYw4?GLZb)<)80>u=cP3SIEtZOW*k)nswforjCX z0taxd*ftDDwCw^@lQQ7d$-Rsg3AGKeQA9$J)_MYEU#5z+1-lsXPZ!BH@&S13yKr zwwNdlHn{c{(^S{T>0$iMkayW=G`Oz(Ffb05km4o5pl2gyp3l3wp+V(}a88@8yW;-b zt@JC7VKio`2{gZQ<|;jRe00a!yv`nr=aQ`(oLn|NHuDayvl03nZ-oP^?D{4|nenIU z1?dijulxM>N+RaI`sIudLl*+}xrYVSyba=`Y|`e^grm`Kl%Lu-S@%;|(#cTjj|#vD zUU_GE`};*NOZ+)rtgNw(UhW-7)tlexd8dUat$l3;sgm1Ge=ekwJmvXjyQN^nQ-;Lf zP*h34gu811ihYUL5)xu$Fl^nXgA%Q)8+NIgK(;rdW<0;oje6GPo9wd}Q7R#>t2|~^ z1NGmq$lRf?Q6T;v6Q`hcJwr^E10RQx=Decmlc)`xdJx|iGei46>v+@i!mbz_Q7xS= zRR9VnbY+RqS@gsM-e)l_j`YptaMvocK$sU-;L*&h7e zFUG#tSK2vQtHdVmka-#5@j;OUGx0F2ufv^KV0n0i z7cw6BB3fPf43mHvSDSj;2gbSBga0DKs?7+RMe(T3cV9ei;@3!6ICU+XtRC z%3--!D}MjgJnUG%)+{v~^qOvZZ4_W3$bx)--;@UAIdN#YbJi?!Xd0STmNEUq8Euo_gY9P_e^OpftQj=bQpbF>-TErOllY zg>ObmMe&Rw{PU0b-tLH9a=4h~4{lFtI^H>6RJZIGdMIh5Q5q;koQ%LbcLUr7|DTno>3*IA0?V}$He(&dvNSGGRejy@Y? zP{D@8Py0>j!s^+=nkmu`zwS@=(N6v*JUN1hgwa^7;upGS?u3&cs$kaNH^SFaRHCIH zA1QRa%O2Cv)bQ{O#Y;k$=92M>;se;0MARh3!PtOD5w@l3&%fQNP``D2B#>q} z@4K$$Kd(E=_|a`5=HKaj4PFsPijI!E|{+;U2y~s_GC=2^QmT5^^!~znfhfx_xXHH)HZxrH_NJ3 zJhKLV>=}8b+=sCMxW@qa*SHQ;@WzIhCEN+){+h%6Vycg>G>w%O{`PSVpkw1FimCB(<>2 zy$AM)?@F@Q!e9OMi(MWhOCN>CV~PF;m*PT^KWR7a+?adFX+|D=yj>{#l3CzZcv%#0 z1nJJ=k4L@0Y}fPb=o zZXR#4Kn@l>3#NavSVrpmhPt?Tbol=Jsx*Dw$~lLK^nm4=%(^&of; z3)kKM{W|>mrh>Vx2*9yma=0lcE3P^5%-83?w-Vo2sQ>;@fj1`v-%RO$PQt+{ Ue`*K5=^zZp6-cV)mN`xq(Y>vj)_Hp_P7ikQ;5JA297BLxfp-WlZ=n{ z0{q7!XwZ2dojSTOv7w`yY0pPmHeEb4G({3b61pJV#FJux^4`|h$F7N}rJ59vrE6HF z&83%Yp^Dm85;xO}{m;P2Ua71T49LtrV8xc;>Fn1-&YLbn#wvDQ+4l*Elf zVkqXD)>x6#P_T?mB1IgZqMn1FB6sb3Vc2irE9|F@Gi%~2I;%~G7ws>qRFwscDeTLN z>#SQ41pHx1IGLQrf)Akz`?iutUZLb%`=se?qTYTcQstYd zwG#1@>|XcpJn_GPeNz|W=ACGjsjNxO{ot$*y7+Ahn*&O}SYK+p3HEWxWOvqv{viF^ zt#WIgg~_b8t<7o)>fKPdE=abLR0QwhcnTE>(ZOklqg=NP#bb0unbmBQl3Bf=@C`1i zgqJZ+Q-eLG_(^+|66M}ORJ;iGRs5%vPWyh**93`@Eb959?*%qlKoHI{+ll(oqKI4S zX*vPh*m|GbHXWddsd`%!LT3&Xma+#F`PJ~WXX@;kV|tlJb#{DGPxay;+V+kn4cNaL z=OBNq{zrX47nxZ7jyvnIr+q9JJkE^C`Ftq`cxhJNDE89CyD?m zW1b$4Gi)D$5(^Q1xuH}Hry%o91o{r~fnAht#~d{i+onWW7m_XgBF0b7j!vRqY~EO2 zv8Fjm-cvRX0`zPgA+CkuwCy2!$O{}{yX)_r$io61p_$8VIiY~(v!ssNGF?CZik$zF ze>3an{EN`(RU$j5Z^xIh>0-b1aV}@{Z`xuoE6k$<1>>+Ig+@dsoS4iiaTCpesItaG zvxn&XXI!TCJA4(@n+laZV0+w}Fy@*65F6V z1i2KgSc6}%SYNqZHE3-xS!T{@wZh8n3W`*w++2yNIry0wZ{g} z<;WNSax*YbKKTvh3R35aCKWt!6w}Vh7#`REDpX+?&bxr)P1$a+Tg?$VSGdyc z`>u2HI{~q$s5>^G>&<@O`3n=6Y(-w1nqdz{1{96YmoV%c{_e6fAA$d#RqPYdj;2~` ziX5(0O4s!63z@5P<3rom*H%DWzd6ac_h&%WeT(PBT{}|k;mn4tjo0oJ4E0y=5wi|E z@u2?JE5h7P14%j3w~ViAp*PN=scxWjH=W2$VN*4ojT<06xmx=A;A<2RGg))7GoxUz-UIx2@VOFY%NIRasm zF&7h{23u4AS}vysF-noglKGlcr(3!u$WF~}C(WIacZi?ov_8&67we`B;L(XyoAbMb z?1$UYrb$QazTzzT+->UGh8rv_^b-xqI6Y3fBwD8dCtYIMch6{+(NEb+uz@Ck%AZe@ zoBlA+u}31{OsvD+3l8S#d8*Jhrtzt;zSHXH8z`YV3-^}?vCmJWz5^ey>_V**>Frx$ zGh|^Arc{G`pn)034epkj=ISz>`k7d4{-zA(L**iu#uo;kCfcui=b0Oei(p>uI)^s$ zm?r7)e%o)V11-CY4zPZIWW7fa#1B})$7$`qE zw>tf#y7e+@rq9V9f7epI8dsgJZWFE#O&ECsw+Q#yA__vXIvfUXYX8%tgQ=K$wJndvXq-Gvu=iJl+ z;Aa3NmgZ?k=|s$JZL&{vcSi!ilCdSYi;l^BsrNv9YP4RbT`^$Q_S2W1>w?sCU(PoU z%3|LocKc>YV`5b_gvR}JVf`d~z8_iq=uA4jSM}HIE5p?v=KA``bqu~AF zC~Btn&QW^d_-JUYWN2vIe|QUGhkzGu;zip>eQ(R{VB=3GLEOw1t^f+TQG#Of%@CBv zFo#7ug;COmhSa9aA}t*Z4*xntZNCgCDHADV$2K-rb1>uK27onb>~{gvC2S zvJWJ|yO=va)8xfeI~LNdB%V4U&h$LSdQ2^yH+@Jfso732YeApq*CiHy(C_R&!rQ>jg3 zr5vkytH|Zqta0k{z$O>li%1O~YFD0G(QP^l8W)qr;dmMs(>|sUFujmnlbPcV^g2Ld zhsFo%(y+Q%uIEWEv$pS|(o1+G;PdCLr|K??O@MaB+TuEPp&o~0HOWw8E`%BnmEnRR zwP#7qE4?1IT=n2jPsRKV#jvSP^p~prisJC1px}R1PM=O6avRW@2udPUp%;JjNMb- zr3wtO>l=lkwr0xLErS#op~nk)V}e*?St(GpC$;gCY*bpbBzZdsRji@3K4-WizkL-HlHEy}P}$JNdq&`s zv9eTF#DJ+6m`N1eMEjJ9*00HRqLx;KzmVV;TZ*lcZ_RkKU^;vc)v`B5K?U=W2lLKs zumVIjpQ1Oh)yS?$jwTA^;C7?Z<8Aa7o;ekTd$zTZ17uPgy z%0AHse?acErg<)TGB=Ab7tYJxszzUBB13t;L{| zm@mS@7F@$;TRZKPTgN4lBS7t-iS?VWXk(K`I%H|+^(;ztTBxm%h{}7ZNa#DssnyHu zOHF|9Izmvti>9PcIKRCzb*LPjvfzoKz(~sny?MuZGkAvC#B%R_ypES=26exkt-flu zRe<#SyqaNB=@0AlLHQz+>l_X5U5 zWG)-k+gLt2#Q%n1^S>22G`klN^H@=|izsSVfTwcz^nY^HeSoxV(Zljcvwm1(;Zk4n zpKw=dzhSd0?`)(P4D@!H<@O9x({|2gO7uBU9H-|K7&ncwFjzZboh2mRoenhr0f6HGw+5snk%AZv3Wex2>1I~ zW-(2{bL8^La&FR7M_JZ^Lq+DWQX+) z90^o&yZps^m0jt_gju!9H=IX!9{PcMjNKdNXW@lKz4+jYaHDd@6__AUdt=xKtdkF+ zuE?$*J|K*(iuaVNJWTV1i|5bDI=vVsyaOAKRW+u*BC5K{l}R$N1FUR)3ygcE3?C40 z3%k4#5nk)Cr$B%Et-_w{?k$=64R!+tZJ%*t3;<nR?WOt|pm8*qcyB zMiElO;@>|u${H-z8Z=W-gEbF*EXjL)$Z}Z9lQQKjUBI@{nHYRLe4D*cQcnq`cD*yv z>3);O1p%l82qY5%f7_d)7HFEtuxa#b2c5O5V2zP!hBI_W^kWh1+YNg@#5t2BN4{5I zw}B?|YeTOSWzxp;#OL_lF}P;}=T*hvf^MBetfuN~AH02CWgcF)8IM@u!Rf6ESb4bz zMqtX3=$NZijCr_TxV{tY%<}}^@cOu_Y8qB!9E8DrYA-JYKPfI>XDN>6!XoF#;`$M| zbBvHI#b`|*R?VvT8~X=JZd&td z6y6=i=QQ0wxLDKNh=fD}&B7TDF@0{#p+~65&_r>&OAcb~Knr{maQAX?wt+%jneO53 zwtn=hO%ZxR5O&Ar`N+xx=S_nFIhJ|ft5XAo2i*VWZ4K`z*RlSHNDWprteB5tQXC!U zqeIb2KW1;}Truz-*`2q})Ek5=qYW;SpTB{hPZzPosQ-vt1_2S8AnHH*6v6={`buI$CQRp5FnB%fe1ckhJS$8UC5*yEHt$7 zKN1(mzfu1HH^~uh%%YETJ~9I_`k(!2V?^vSQ~%TUUr02P{{*CH4-dz*2oMY1W1B1s z5Q7$o(4uD{)>cQypuqgM^oB_OQ!#n0a3YeJh!EQ>^ccmDAwSpu0{_K-AAiW>&;4G1 Zw#a|xJig;A{htP2L@JmD2f^@I`5$sXV0i!l diff --git a/server/target/server-1.0-SNAPSHOT.jar b/server/target/server-1.0-SNAPSHOT.jar index 7deb4df9fc3c1467a9b88d7f14c1b663abb67bc8..ba142348e4d4c29c038e4bcbc8247c383b8c91cc 100644 GIT binary patch delta 5432 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 diff --git a/web-client/src/main/resources/static/css/style.css b/web-client/src/main/resources/static/css/style.css index 1b04c69..1ab084c 100644 --- a/web-client/src/main/resources/static/css/style.css +++ b/web-client/src/main/resources/static/css/style.css @@ -84,14 +84,18 @@ h1 { } .secondary { - background: var(--card-bg); /* Use card background for join button */ - color: white; /* Ensure text is visible */ - border: 1px solid var(--border); /* Add a border to distinguish it */ + background: var(--card-bg); + /* Use card background for join button */ + color: white; + /* Ensure text is visible */ + border: 1px solid var(--border); + /* Add a border to distinguish it */ } /* Add hover effect for secondary button too */ .secondary:hover { - background: #27272a; /* Slightly lighter on hover */ + background: #27272a; + /* Slightly lighter on hover */ } .danger { @@ -101,8 +105,9 @@ h1 { } input[type="text"] { - width: 100%; - box-sizing: border-box; /* This ensures padding is included in width */ + width: 100%; + box-sizing: border-box; + /* This ensures padding is included in width */ padding: 12px; margin: 10px 0; background: #27272a; @@ -180,12 +185,20 @@ input[type="text"]:focus { background: #3f3f46; } +.game-actions { + display: flex; + gap: 10px; + margin-top: 20px; +} + .cell.x { - color: var(--secondary); /* Pink for X */ + color: var(--secondary); + /* Pink for X */ text-shadow: 0 0 10px rgba(219, 39, 119, 0.6); } .cell.o { - color: var(--accent); /* Green for O */ + color: var(--accent); + /* Green for O */ text-shadow: 0 0 10px rgba(16, 185, 129, 0.6); -} +} \ No newline at end of file diff --git a/web-client/src/main/resources/static/js/app.js b/web-client/src/main/resources/static/js/app.js index e6c6033..b3cd91f 100644 --- a/web-client/src/main/resources/static/js/app.js +++ b/web-client/src/main/resources/static/js/app.js @@ -6,46 +6,17 @@ const joinBtn = document.getElementById('join-btn'); const joinInput = document.getElementById('join-code'); const displayCode = document.getElementById('display-code'); const turnStatus = document.getElementById('turn-status'); -const surrenderBtn = document.getElementById('surrender-btn'); -const cells = document.querySelectorAll('.cell'); +const leaveBtn = document.getElementById('leave-btn'); -let mySymbol = ''; -let ws = null; -let currentCode = ''; +// ... (other vars) -function connect() { - const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - ws = new WebSocket(`${protocol}//${window.location.host}/game`); +// ... - ws.onopen = () => { - statusDiv.textContent = 'Connected to Server'; - statusDiv.style.color = '#10b981'; - }; - - ws.onclose = () => { - statusDiv.textContent = 'Disconnected. Reconnecting...'; - statusDiv.style.color = '#ef4444'; - setTimeout(connect, 2000); - }; - - ws.onmessage = (event) => { - handleMessage(event.data); - }; -} - -function send(msg) { - if (ws && ws.readyState === WebSocket.OPEN) { - ws.send(msg); +leaveBtn.addEventListener('click', () => { + if (confirm('Are you sure you want to leave the lobby?')) { + send('LEAVE'); + resetGame(); } -} - -createBtn.addEventListener('click', () => { - send('CREATE'); -}); - -joinBtn.addEventListener('click', () => { - const code = joinInput.value.trim(); - if (code) send(`JOIN ${code}`); }); surrenderBtn.addEventListener('click', () => { @@ -54,13 +25,7 @@ surrenderBtn.addEventListener('click', () => { } }); -cells.forEach(cell => { - cell.addEventListener('click', () => { - const r = cell.dataset.r; - const c = cell.dataset.c; - send(`MOVE ${r} ${c}`); - }); -}); +// ... function handleMessage(msg) { console.log("Received:", msg); @@ -83,6 +48,13 @@ function handleMessage(msg) { case 'START': updateStatus("Game Started! X goes first."); break; + case 'RESTART': + updateStatus("Game Restarted! X goes first."); + cells.forEach(c => { + c.textContent = ''; + c.className = 'cell'; + }); + break; case 'BOARD': const boardStr = msg.substring(6); updateBoard(boardStr); @@ -97,11 +69,21 @@ function handleMessage(msg) { break; case 'WIN': const winner = msg.substring(4); - alert(`Game Over! Winner: ${winner}`); - resetGame(); + setTimeout(() => { + if (confirm(`Game Over! Winner: ${winner}\nDo you want to play again?`)) { + send('RESTART'); + } + }, 100); break; case 'DRAW': - alert("Game Over! It's a draw!"); + setTimeout(() => { + if (confirm("Game Over! It's a draw!\nDo you want to play again?")) { + send('RESTART'); + } + }, 100); + break; + case 'OPPONENT_LEFT': + alert("Opponent has left the lobby."); resetGame(); break; case 'ERROR:': diff --git a/web-client/src/main/resources/templates/index.html b/web-client/src/main/resources/templates/index.html index d29f35e..89a0d3d 100644 --- a/web-client/src/main/resources/templates/index.html +++ b/web-client/src/main/resources/templates/index.html @@ -1,16 +1,19 @@ + TicTacToe - + +

TicTacToe

- +
Connecting to server...
@@ -26,7 +29,6 @@
- - + + \ No newline at end of file diff --git a/web-client/target/classes/static/css/style.css b/web-client/target/classes/static/css/style.css index 1b04c69..1ab084c 100644 --- a/web-client/target/classes/static/css/style.css +++ b/web-client/target/classes/static/css/style.css @@ -84,14 +84,18 @@ h1 { } .secondary { - background: var(--card-bg); /* Use card background for join button */ - color: white; /* Ensure text is visible */ - border: 1px solid var(--border); /* Add a border to distinguish it */ + background: var(--card-bg); + /* Use card background for join button */ + color: white; + /* Ensure text is visible */ + border: 1px solid var(--border); + /* Add a border to distinguish it */ } /* Add hover effect for secondary button too */ .secondary:hover { - background: #27272a; /* Slightly lighter on hover */ + background: #27272a; + /* Slightly lighter on hover */ } .danger { @@ -101,8 +105,9 @@ h1 { } input[type="text"] { - width: 100%; - box-sizing: border-box; /* This ensures padding is included in width */ + width: 100%; + box-sizing: border-box; + /* This ensures padding is included in width */ padding: 12px; margin: 10px 0; background: #27272a; @@ -180,12 +185,20 @@ input[type="text"]:focus { background: #3f3f46; } +.game-actions { + display: flex; + gap: 10px; + margin-top: 20px; +} + .cell.x { - color: var(--secondary); /* Pink for X */ + color: var(--secondary); + /* Pink for X */ text-shadow: 0 0 10px rgba(219, 39, 119, 0.6); } .cell.o { - color: var(--accent); /* Green for O */ + color: var(--accent); + /* Green for O */ text-shadow: 0 0 10px rgba(16, 185, 129, 0.6); -} +} \ No newline at end of file diff --git a/web-client/target/classes/static/js/app.js b/web-client/target/classes/static/js/app.js index e6c6033..b3cd91f 100644 --- a/web-client/target/classes/static/js/app.js +++ b/web-client/target/classes/static/js/app.js @@ -6,46 +6,17 @@ const joinBtn = document.getElementById('join-btn'); const joinInput = document.getElementById('join-code'); const displayCode = document.getElementById('display-code'); const turnStatus = document.getElementById('turn-status'); -const surrenderBtn = document.getElementById('surrender-btn'); -const cells = document.querySelectorAll('.cell'); +const leaveBtn = document.getElementById('leave-btn'); -let mySymbol = ''; -let ws = null; -let currentCode = ''; +// ... (other vars) -function connect() { - const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - ws = new WebSocket(`${protocol}//${window.location.host}/game`); +// ... - ws.onopen = () => { - statusDiv.textContent = 'Connected to Server'; - statusDiv.style.color = '#10b981'; - }; - - ws.onclose = () => { - statusDiv.textContent = 'Disconnected. Reconnecting...'; - statusDiv.style.color = '#ef4444'; - setTimeout(connect, 2000); - }; - - ws.onmessage = (event) => { - handleMessage(event.data); - }; -} - -function send(msg) { - if (ws && ws.readyState === WebSocket.OPEN) { - ws.send(msg); +leaveBtn.addEventListener('click', () => { + if (confirm('Are you sure you want to leave the lobby?')) { + send('LEAVE'); + resetGame(); } -} - -createBtn.addEventListener('click', () => { - send('CREATE'); -}); - -joinBtn.addEventListener('click', () => { - const code = joinInput.value.trim(); - if (code) send(`JOIN ${code}`); }); surrenderBtn.addEventListener('click', () => { @@ -54,13 +25,7 @@ surrenderBtn.addEventListener('click', () => { } }); -cells.forEach(cell => { - cell.addEventListener('click', () => { - const r = cell.dataset.r; - const c = cell.dataset.c; - send(`MOVE ${r} ${c}`); - }); -}); +// ... function handleMessage(msg) { console.log("Received:", msg); @@ -83,6 +48,13 @@ function handleMessage(msg) { case 'START': updateStatus("Game Started! X goes first."); break; + case 'RESTART': + updateStatus("Game Restarted! X goes first."); + cells.forEach(c => { + c.textContent = ''; + c.className = 'cell'; + }); + break; case 'BOARD': const boardStr = msg.substring(6); updateBoard(boardStr); @@ -97,11 +69,21 @@ function handleMessage(msg) { break; case 'WIN': const winner = msg.substring(4); - alert(`Game Over! Winner: ${winner}`); - resetGame(); + setTimeout(() => { + if (confirm(`Game Over! Winner: ${winner}\nDo you want to play again?`)) { + send('RESTART'); + } + }, 100); break; case 'DRAW': - alert("Game Over! It's a draw!"); + setTimeout(() => { + if (confirm("Game Over! It's a draw!\nDo you want to play again?")) { + send('RESTART'); + } + }, 100); + break; + case 'OPPONENT_LEFT': + alert("Opponent has left the lobby."); resetGame(); break; case 'ERROR:': diff --git a/web-client/target/classes/templates/index.html b/web-client/target/classes/templates/index.html index d29f35e..89a0d3d 100644 --- a/web-client/target/classes/templates/index.html +++ b/web-client/target/classes/templates/index.html @@ -1,16 +1,19 @@ + TicTacToe - + +

TicTacToe

- +
Connecting to server...
@@ -26,7 +29,6 @@
- - + + \ No newline at end of file diff --git a/web-client/target/web-client-1.0-SNAPSHOT.jar b/web-client/target/web-client-1.0-SNAPSHOT.jar index 3e284e49deef76a8f8d632d4e36a0ee372bbb6c7..3890a4099e3e064e1d4b9a87e81060556a28e94a 100644 GIT binary patch delta 7183 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 delta 7288 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 LE~GXn@$dftp)pNT