Discuss Scratch
- Discussion Forums
- » Help with Scripts
- » how do i make a win detector for connect 4?
- Leonkghunt
-
Scratcher
87 posts
how do i make a win detector for connect 4?
im working on a board games game, one is connect four. i have done the place scripts but i can't find out how to make it detect if you win without adding a script that checks EVERY SINGLE SPOT you can possible win. this method would work for naughts + crosses but connect 4 has way too much spots. link https://scratch.mit.edu/projects/691213617/
- deck26
-
Scratcher
1000+ posts
how do i make a win detector for connect 4?
You need a grid system - https://scratch.mit.edu/projects/75428464/ may help although it doesn't detect diagonal lines.
Essentially the method would be to start with the bottom left position and, if occupied, check for an adjacent set of 3 matches to the right and above. Also check diagonals going up and left and up and right but watching out for edges. Then you move on the second position and repeat and so on. Once you're checked the bottom row do the same for the second row. That way you never need to check for anything but a line starting with the current position and checking in directions pointing away from the positions already checked. In fact you can reduce the checking by seeing if the current position matches an adjacent one that has already been checked. There's no need to check for a vertical line, for example, if the position below matches the current position since that line would already have been checked.
Essentially the method would be to start with the bottom left position and, if occupied, check for an adjacent set of 3 matches to the right and above. Also check diagonals going up and left and up and right but watching out for edges. Then you move on the second position and repeat and so on. Once you're checked the bottom row do the same for the second row. That way you never need to check for anything but a line starting with the current position and checking in directions pointing away from the positions already checked. In fact you can reduce the checking by seeing if the current position matches an adjacent one that has already been checked. There's no need to check for a vertical line, for example, if the position below matches the current position since that line would already have been checked.
- RT_Borg
-
Scratcher
1000+ posts
how do i make a win detector for connect 4?
Hi Leonkghunt,
deck26 has put you on the right track, to use a grid system. I think you can make the checking a little easier on yourself, though.
Really, you don't have to check the entire board for a win every time a spot is filled. You only have to check whether the new spot completes a connect-4. (Any other would have already been detected on the turn it was played, and ended the game.)
So you can look around only the newly filled spot.
I hope this helps. Happy to answer any questions.
– RT_Borg
deck26 has put you on the right track, to use a grid system. I think you can make the checking a little easier on yourself, though.
Really, you don't have to check the entire board for a win every time a spot is filled. You only have to check whether the new spot completes a connect-4. (Any other would have already been detected on the turn it was played, and ended the game.)
So you can look around only the newly filled spot.
- downward (of the same color) of 3 (plus the new spot) is a vertical connect-4
- leftward count + rightward count (of the same color) of 3 or more (plus the new spot) is a horizontal connect-4
- north-east count + south-west count of 3 or more => connect-4
- north-west count + south-east count of 3 or more => connect-4
I hope this helps. Happy to answer any questions.
– RT_Borg
- poke_cat_guy123
-
Scratcher
61 posts
how do i make a win detector for connect 4?
when green flag clickedhope i did the blocks right
forever
if <> then
<[] = [4]>
broadcast [ v]
end
end
- Leonkghunt
-
Scratcher
87 posts
how do i make a win detector for connect 4?
Hi Leonkghunt,Hey! sorry for the late response. but I don't know how to make it detect if it is touching 3 of the same type of counters?
deck26 has put you on the right track, to use a grid system. I think you can make the checking a little easier on yourself, though.
Really, you don't have to check the entire board for a win every time a spot is filled. You only have to check whether the new spot completes a connect-4. (Any other would have already been detected on the turn it was played, and ended the game.)
So you can look around only the newly filled spot.
- downward (of the same color) of 3 (plus the new spot) is a vertical connect-4
- leftward count + rightward count (of the same color) of 3 or more (plus the new spot) is a horizontal connect-4
- north-east count + south-west count of 3 or more => connect-4
- north-west count + south-east count of 3 or more => connect-4
I hope this helps. Happy to answer any questions.
– RT_Borg
- RT_Borg
-
Scratcher
1000+ posts
how do i make a win detector for connect 4?
The link in your first post doesn't seem to be shared?
How to Share a Project Link
To share a project link, click the orange “Share” button at the top of the page. Then in “See Project Page” click Copy Link near the bottom, and Copy Link again on the pop-up, and paste it in a message.
– RT_Borg
How to Share a Project Link
To share a project link, click the orange “Share” button at the top of the page. Then in “See Project Page” click Copy Link near the bottom, and Copy Link again on the pop-up, and paste it in a message.
– RT_Borg
- Leonkghunt
-
Scratcher
87 posts
how do i make a win detector for connect 4?
The link in your first post doesn't seem to be shared?Sorry, its shared now. also you may notice that the counters are getting stuck at the top, but that's just because im working on a new row finder method that's not finished.
How to Share a Project Link
To share a project link, click the orange “Share” button at the top of the page. Then in “See Project Page” click Copy Link near the bottom, and Copy Link again on the pop-up, and paste it in a message.
– RT_Borg
- NeonG4
-
Scratcher
1000+ posts
how do i make a win detector for connect 4?
This might seem complex, but you need to store every tile in a list, then scan the list for when there are 4 tiles in a row.The link in your first post doesn't seem to be shared?Sorry, its shared now. also you may notice that the counters are getting stuck at the top, but that's just because im working on a new row finder method that's not finished.
How to Share a Project Link
To share a project link, click the orange “Share” button at the top of the page. Then in “See Project Page” click Copy Link near the bottom, and Copy Link again on the pop-up, and paste it in a message.
– RT_Borg
- cIoudyness
-
Scratcher
500+ posts
how do i make a win detector for connect 4?
let’s assume your grid is 7x6 like standard
your grid list will obviously have 42 values.
first for new coin (or piece) placing,
here’s the snap-to x distance script, something like this:
then maybe to record the piece placed you can do something like this:
your grid list will obviously have 42 values.
first for new coin (or piece) placing,
here’s the snap-to x distance script, something like this:
set [var v] to (round ((x position) / (7)))conveniently the x snapping also gives you a variable that tells you the lane of the piece that went in. (you may need to add 3 or 4 to it to give you a lane 1-7)
set x to (((var) * (7)) + (0)) // experiment with this 0 to get an offset that you like
then maybe to record the piece placed you can do something like this:
find empty spacewill code something for checking for a win in a moment. if something doesn’t work pls tell
define find empty space
set [i v] to (0) // local variable
check
if <(i) = (-1)> then
invalid column: already filled to the top! ::sensing
broadcast [restart turn v]
stop [this script v]
end
replace item ((lane) + ((i) * (7))) of [list v] with (player this turn) // perhaps 1 for red or 2 for blue
set y to ((150) - ((i) * (30))) // experiment with values. may be very far off
define check
if <(item ((lane) + ((i) * (7))) of [list v]) = (0)> then
check
else
change [i v] by (-1)
stop [this script v]
end
Last edited by cIoudyness (July 11, 2022 18:39:13)
- cIoudyness
-
Scratcher
500+ posts
how do i make a win detector for connect 4?
I know I promised a win detector earlier. still working
if anyone else can help, we’re trying to check seven spots in a line, from a grid list with the new tile at the center of said seven spots. we need to do this for up-down, top right to bottom left, right to left, and bottom right to top left.
if anyone else can help, we’re trying to check seven spots in a line, from a grid list with the new tile at the center of said seven spots. we need to do this for up-down, top right to bottom left, right to left, and bottom right to top left.
- cIoudyness
-
Scratcher
500+ posts
how do i make a win detector for connect 4?
first let’s set some variables to more easily comprehensible names. of course the win detection script should go right after the new piece has been placed (put this code right after the previous script sets y, or better yet, set your variables to these names at the start to avoid confusion)
for this you need to use 0 for one side, 1 for the other and use the empty value “” to represent nothing being in a certain list value. so when you set your list fill it with 42 empty values.
that should be it? not really any chance that all this code works but the point is to show the process:
get your row, your column and your spot, then
check 4 of the 8 directions. take 3 steps up (if possible) then go down 7, checking for consecutive player piece stings along the way. do the same for two diagonals and the horizontal axes.
most of these variables should be made ‘for this sprite only’
any questions or something isn’t working (very likely) please let me know.
set [opposite color v] to ((1) - (player that just moved))you may just want to set this with an if player that just moved is 1, then opposite is 2. else vice versa
for this you need to use 0 for one side, 1 for the other and use the empty value “” to represent nothing being in a certain list value. so when you set your list fill it with 42 empty values.
set [column v] to (lane)
set [row v] to (i) // worth noting that this is actually 1 less than the perceived row we see. it makes calculation easy
find spot
set [spot v] to (i)
check win?
define find spot
set [i v] to (((row) * (7)) + (column))
define check win
set [checking v] to (0)
repeat (4)
set [x change v] to ([sin v] of (checking))
set [y change v] to (round ([cos v] of (checking)))
set [count v] to (0)
try for max (3)(1)
find spot
set [x change v] to ((0) - (x change))
set [y change v] to ((0) - (y change))
set [longest string v] to (0)
try for max ((count) + (4))(-1)
if <(longest string) = (4)> then
stop [this script v]
// we’ve already broadcasted this to other sprites in the code below
end
change [checking v] by (45)
end
define try for max (input)(count #)
repeat (input)
change [count v] by (count #)
change [column v] by (x change)
change [row v] by (y change)
if <(count #) = (-1)>then
find spot
if <(item (i) of [list v]) = (player we’re checking for right now::sensing)> then
change [longest string v] by (1)
else
set [longest string v] to (0)
end
if <(longest string) = (4)> then
broadcast [yay there’s a winner! v]
stop [this script v]
end
if <not <(longest string) > (count)>> then
stop [this script v]
end
if <<(row) < (0)> or <(row) > (6) >> then
opposite move // this has another use elsewhere
stop [this script v]
end
if <<(column) < (0)> or <(column) > (7)>> then
opposite move // here just returns to the last position
stop [this script v]
end
find spot
if <(item (i) of [list v]) = (opposite color!!! :: sensing) > then
opposite move
stop [this script v]
end
define opposite move
change [column v] by ((0) - (x change))
change [row v] by ((0) - (y change))
that should be it? not really any chance that all this code works but the point is to show the process:
get your row, your column and your spot, then
check 4 of the 8 directions. take 3 steps up (if possible) then go down 7, checking for consecutive player piece stings along the way. do the same for two diagonals and the horizontal axes.
most of these variables should be made ‘for this sprite only’
any questions or something isn’t working (very likely) please let me know.
Last edited by cIoudyness (July 11, 2022 22:20:44)
- cIoudyness
-
Scratcher
500+ posts
how do i make a win detector for connect 4?
I’ve just noticed this is an insane amount of scratchblocks. (sorry in advance to any mobile users) just didn’t feel like coding a connect 4 project myself.
of course once you implement all this code I should bug fix it again in an actual project
of course once you implement all this code I should bug fix it again in an actual project
- RT_Borg
-
Scratcher
1000+ posts
how do i make a win detector for connect 4?
(Edit: I see cIoudyness has posted logic like what you'd use with this, while I was writing my message.)
Hi Leonkghunt and cIoudyness,
For most problems like this…
I would create a list to store all the states of the slots in the board. The first column (first 6 items) in positions 1-6. The second column (next 6 items) in positions 7-12, etc.
Then create custom blocks to make it easy to store and retrieve these states
Now that it's easy to get and set states (e.g., “PLAYER-1” or “PLAYER-2”) the logic to look for states relative to a given position should be more straightforward.
– RT_Borg
Hi Leonkghunt and cIoudyness,
For most problems like this…
I would create a list to store all the states of the slots in the board. The first column (first 6 items) in positions 1-6. The second column (next 6 items) in positions 7-12, etc.
(board :: list)
when green flag clicked
delete all of [board v]::list
repeat (42) // 7 columns * 6 rows
add [EMPTY] to [board v]
end
Then create custom blocks to make it easy to store and retrieve these states
define Set State | row: (row) col: (col) state: (state)
set [list-index v] to ( (row) + (((col) - (1)) * (6)))
replace item [list-index v] of [board v] with (state)
(state) // most recent state from Get State
define Get State | row: (row) col: (col) |> state
set [list-index v] to ( (row) + (((col) - (1)) * (6)))
set [state v] to (item [list-index v] of [board v])
Now that it's easy to get and set states (e.g., “PLAYER-1” or “PLAYER-2”) the logic to look for states relative to a given position should be more straightforward.
– RT_Borg
Last edited by RT_Borg (July 11, 2022 22:35:49)
- RT_Borg
-
Scratcher
1000+ posts
how do i make a win detector for connect 4?
All that said (and I think it's a lesson worth the time, whether you use it here or not)… for this problem:
I made a remix of the project, and a Detector sprite with costumes that draw lines in each direction.
FOR HELP: Board Games remix
https://scratch.mit.edu/projects/713449653
If centered at a position on the board, each of these costumes will draw a line that overlaps 4 slots in the costume direction (that will make sense if you look at Detector in the remix).
Assuming you can easily position Detector at the spots on your board, move it to each spot. On the spot, switch to each costume.
Create counters:
Then for each spot/costume, you'll get the Indicator clones to do the counting for you.
In Connect 4 Counter (which is also the sprite that represents token clones):
Edit: I first forgot to check “Costume name = Red/Yellow” when receivers change these counts. (And really, both counts should probably just get updated with a single broadcast.)
Anyway, I wanted to share this alternate way to think about solving the problem.
If you want to try this, but can't find a good way to position the Detector and initiate the counting, let me know and I'll share some ideas about that.
– RT_Borg
I made a remix of the project, and a Detector sprite with costumes that draw lines in each direction.
FOR HELP: Board Games remix
https://scratch.mit.edu/projects/713449653
If centered at a position on the board, each of these costumes will draw a line that overlaps 4 slots in the costume direction (that will make sense if you look at Detector in the remix).
Assuming you can easily position Detector at the spots on your board, move it to each spot. On the spot, switch to each costume.
Create counters:
(TOUCHING-RED-COUNT) // For All Sprites
(TOUCHING-YELLOW-COUNT) // For All Sprites
Then for each spot/costume, you'll get the Indicator clones to do the counting for you.
set [TOUCHING-RED-COUNT v] to (0)
broadcast [Count Red v] and wait
if <(TOUCHING-RED-COUNT) > (3)> then
... / win for red. broadcast. stop script
end
...
set [TOUCHING-YELLOW-COUNT v] to (0)
broadcast [Count Yellow v] and wait
if <(TOUCHING-YELLOW-COUNT) > (3)> then
... / win for yellow. broadcast. stop script
end
In Connect 4 Counter (which is also the sprite that represents token clones):
Edit: I first forgot to check “Costume name = Red/Yellow” when receivers change these counts. (And really, both counts should probably just get updated with a single broadcast.)
when I receive [Count Red v]
if <((costume [name v]::looks) = [Red]) and (touching [Detector v] ?)> then
change [TOUCHING-RED-COUNT v] by (1)
when I receive [Count Yellow v]
if <((costume [name v]::looks) = [Yellow]) and (touching [Detector v] ?)> then
change [TOUCHING-YELLOW-COUNT v] by (1)
Anyway, I wanted to share this alternate way to think about solving the problem.
If you want to try this, but can't find a good way to position the Detector and initiate the counting, let me know and I'll share some ideas about that.
– RT_Borg
Last edited by RT_Borg (July 12, 2022 03:03:54)
- cIoudyness
-
Scratcher
500+ posts
how do i make a win detector for connect 4?
snipnow that I’ve realized how utterly dumb using code alone is,
perhaps a teeny suggestion is to just have two costumes for straight and diagonal and just turn to the currently checked direction
very inconsequential though
Last edited by cIoudyness (July 12, 2022 02:24:51)
- RT_Borg
-
Scratcher
1000+ posts
how do i make a win detector for connect 4?
…how utterly dumb using code alone is
Well starting from lists and positions, like you and I both approached it, could simplify some different things, like where tokens should be dropped from and where they should land. It also generalizes better to other board types or similar games.
perhaps a teeny suggestion is to just have two costumes for straight and diagonal and just turn to the currently checked direction
Where were you when I was flipping and moving costume lines
Yes, that would have been easier, especially since the costumes I made have the proper rotation point to do that.I forgot to mention the thing I liked the most when I was inspired by this technique vs lists. When you find a win, the Detector positioned across 4 in a row would be a nice display to the user.
– RT_Borg
- cIoudyness
-
Scratcher
500+ posts
how do i make a win detector for connect 4?
as I remember you’re checking 4 units radiating outward from the newly placed piece. but imagine the uncommon edge case where the new piece completes a four, but not at the edges - imagine a set of stairs missing a step, and we fill in that step! checking starting from the new stair will only show us, say, three including the new piece up, and two including the new piece down…how utterly dumb using code alone is
Well starting from lists and positions, like you and I both approached it, could simplify some different things, like where tokens should be dropped from and where they should land. It also generalizes better to other board types or similar games.perhaps a teeny suggestion is to just have two costumes for straight and diagonal and just turn to the currently checked direction
Where were you when I was flipping and moving costume linesYes, that would have been easier, especially since the costumes I made have the proper rotation point to do that.
I forgot to mention the thing I liked the most when I was inspired by this technique vs lists. When you find a win, the Detector positioned across 4 in a row would be a nice display to the user.
– RT_Borg
forgive me, but is that a fatal flaw within otherwise flawless and well-reasoned coding?? :O
- Leonkghunt
-
Scratcher
87 posts
how do i make a win detector for connect 4?
Hey! I think i created a way to do it, i just need to actually code it in.
- Leonkghunt
-
Scratcher
87 posts
how do i make a win detector for connect 4?
Hey! I think i created a way to do it, i just need to actually code it in.Nevermind, It Didnt Work, And I Found A Flaw.
- RT_Borg
-
Scratcher
1000+ posts
how do i make a win detector for connect 4?
cIoudyness,
If you're using the a list-based method, you only have to check the possibility that include the new spot (not necessarily as an endpoint). Technically it's the same for the touching Detector approach, but probably harder to select the subset to check. That's why I suggested just checking each costume position at every x,y on the board (or at least every one where a token is already placed).
Really, this should all happen so fast, there's no reason to optimize if it takes extra blocks to program.
Leonkghunt,
The easiest way to implement the touching Detector way is probably to store the x,y positions you need the detector to position itself at.
On green flag clicked, delete all, in case the player has entries from a previous playthrough.
You can either fill these x,y positions all in at the start of the game, or have tokens add their x, y to the lists when they get placed on the board.
Then, wherever in the code you determine a win check should be made,
The only receiver for Detect Win is Detector (the new sprite with the lines in each direction).
When Detector receives “Detect Win” it loops through each index (repeat the length of either list, they're the same length). For each x, y pair from the lists, Detector moves to that position,
In a nested loop, it loops through each of its 8 costumes (pointing in each direction) and on each costume uses broadcasts to find if there's a winner
In Connect 4 Counter (the sprite that represents token clones):
If there's a non-clone Connect 4 Counter, you'll have to do something to make sure it doesn't record itself as touching the Detector. Either a clone-id type thing, or make sure it has a different costume to wear when it isn't being used as a template to make a clone. (Happy to suggest if you aren't sure what to do.)
I think that basic idea should work (though it's possible I missed a detail along the way).
– RT_Borg
If you're using the a list-based method, you only have to check the possibility that include the new spot (not necessarily as an endpoint). Technically it's the same for the touching Detector approach, but probably harder to select the subset to check. That's why I suggested just checking each costume position at every x,y on the board (or at least every one where a token is already placed).
Really, this should all happen so fast, there's no reason to optimize if it takes extra blocks to program.
Leonkghunt,
The easiest way to implement the touching Detector way is probably to store the x,y positions you need the detector to position itself at.
(DETECT_X :: list) // For all sprites
(DETECT_Y :: list) // For all sprites
On green flag clicked, delete all, in case the player has entries from a previous playthrough.
You can either fill these x,y positions all in at the start of the game, or have tokens add their x, y to the lists when they get placed on the board.
Then, wherever in the code you determine a win check should be made,
broadcast [Detect Win v] // possibly "and wait", depending on where this is
The only receiver for Detect Win is Detector (the new sprite with the lines in each direction).
When Detector receives “Detect Win” it loops through each index (repeat the length of either list, they're the same length). For each x, y pair from the lists, Detector moves to that position,
define Detect // Run without screen refresh
show
set [i v] to (1)
repeat (length of [DETECT-X v] :: list)
go to x: (item (i) of [DETECT-X v] :: list) y: (item (i) of [DETECT-Y v] :: list)
... // below
end
hide
In a nested loop, it loops through each of its 8 costumes (pointing in each direction) and on each costume uses broadcasts to find if there's a winner
// the nested part
switch costume to [north v]
repeat (8)
set [TOUCHING-RED-COUNT v] to (0)
set [TOUCHING-YELLOW-COUNT v] to (0)
broadcast [Check Touching Detector v] and wait // the clone at ROW, COLUMN will do a test
if <(TOUCHING-RED-COUNT) > (3)> then
... / win for red. broadcast. stop script
end
if <(TOUCHING-YELLOW-COUNT) > (3)> then
... / win for yellow. broadcast. stop script
end
next costume
end
In Connect 4 Counter (the sprite that represents token clones):
when I receive [Check Touching Detector v]
if <((costume [name v]::looks) = [Red]) and (touching [Detector v] ?)> then
change [TOUCHING-RED-COUNT v] by (1)
end
if <((costume [name v]::looks) = [Yellow]) and (touching [Detector v] ?)> then
change [TOUCHING-YELLOW-COUNT v] by (1)
end
If there's a non-clone Connect 4 Counter, you'll have to do something to make sure it doesn't record itself as touching the Detector. Either a clone-id type thing, or make sure it has a different costume to wear when it isn't being used as a template to make a clone. (Happy to suggest if you aren't sure what to do.)
I think that basic idea should work (though it's possible I missed a detail along the way).
– RT_Borg
- Discussion Forums
- » Help with Scripts
-
» how do i make a win detector for connect 4?
Yes, that would have been easier, especially since the costumes I made have the proper rotation point to do that.