Snider-man, on Wed Sep 17, 2008 11:52 AM, said:

In one afternoon, you effectively solved two problems I was working on. Very nice and I *really* appreciate the help.
Two birds with one stone is always nice (well, unless you're a bird). Thanks for the thanks, and you're welcome.
Snider-man, on Wed Sep 17, 2008 11:52 AM, said:
I need to take a look at what you did and wrap my head around it
First, I noted how the player's X and Y coordinates relate to the playfield pixels. This is fairly straightforward for the most part, but can be a little bit tricky because the width and height of the player affects it, as well as which kernel is being used, and the presence of any blank rows or columns within the sprite definition can also affect it (depending on where they are). That's why I used the program that displays the player's coordinates in the score while you move the sprite around.
SeaGtGruff, on Tue Sep 16, 2008 2:42 AM, said:
x < 17 : left of dartboard
x = 17 to 20 : outer red
x = 21 to 24 : middle black
x = 25 to 28 : middle red
x = 29 to 32 : inner black
x = 33 to 36 : center
x = 37 to 40 : inner black
x = 41 to 44 : middle red
x = 45 to 48 : middle black
x = 49 to 52 : outer red
x > 52 : right of dartboard
y < 9 : above dartboard
y = 9 to 17 (16?) : outer red
y = 17 to 25 (24?) : middle black
y = 25 to 33 (32?) : middle red
y = 33 to 41 (40?) : inner black
y = 41 to 49 : center
y = 49 (50?) to 57 : inner black
y = 57 (58?) to 65 : middle red
y = 65 (66?) to 73 : middle black
y = 73 (74?) to 81 : outer red
y > 81 : below dartboard
Then I made a little grid for the dartboard, with the numbers 0 through 5 indicating the red and black circles:
00000000000
00011111000
00122222100
01223332210
01234443210
01234543210
01234443210
01223332210
00122222100
00011111000
00000000000
0 = outside the dartboard
1 = outer red circle
2 = middle black circle
3 = middle red circle
4 = inner black circle
5 = center (red bull's eye)
The nice thing about this particular playfield is that the dartboard is circular, so it's reflected around its horizontal axis, as well as around its vertical axis. This lets us simplify it down to about a quarter of its full size:
000000
000111
001222
012233
012344
012345
And we can also shave off the row and column that are all 0s:
00111
01222
12233
12344
12345
You'll notice that this is essentially the same as the data table I posted:
data points
0, 0, 1, 1, 1
0, 1, 2, 2, 2
1, 2, 2, 3, 3
1, 2, 3, 4, 4
1, 2, 3, 4, 5, 0
end
The only thing I did (aside from putting commas between the values) was to tack on an extra 0 at the end of the table, which I'll get to in a moment.
Going back to the original grid, we can assign column numbers and row numbers to it. For the benefit of the data table, we want the row and column numbers to begin with 0, since the index to the data table will also start with 0:
x012345678x
00000000000 -- x
00011111000 -- 0
00122222100 -- 1
01223332210 -- 2
01234443210 -- 3
01234543210 -- 4
01234443210 -- 5
01223332210 -- 6
00122222100 -- 7
00011111000 -- 8
00000000000 -- x
The "x" rows and columns are where the dart falls outside the dartboard, and they extend for some distance on all sides of the dartboard, so we're essentially taking all the empty space on the four sides of the dartboard and compressing it down into the "x" rows and columns.
Now we want a way to code which circle of the dartboard has been hit. We could use a bunch of IF statements with logical ANDs (&&s) and logical ORs (||s), but that would be kind of messy. It's simpler to use mathematical formulas to convert the dart's X and Y coordinates into the equivalent rows and columns of the dartboard.
Each playfield pixel spans four player pixel positions, so we can divide the player0x position by 4 to get the playfield column number-- except we need to take into account that the playfield doesn't start at the leftmost edge of the screen, and we also want to use the column numbers that we came up with for the grid. So we can start by listing which player0x positions map to which grid columns:
player0x = 0 through 16 ... Column x
player0x = 17 through 20 ... Column 0
player0x = 21 through 24 ... Column 1
player0x = 25 through 28 ... Column 2
player0x = 29 through 32 ... Column 3
player0x = 33 through 36 ... Column 4
player0x = 37 through 40 ... Column 5
player0x = 41 through 44 ... Column 6
player0x = 45 through 48 ... Column 7
player0x = 49 through 52 ... Column 8
player0x = 53 through 255 ... Column x
Now, some of these are technically incorrect-- i.e., for player0x = 0, the tip of the dart is to the right of the dartboard; and player0x = 161 through 255 is equivalent to player0x = 1 through 95-- but I'm assuming that you'll probably be keeping the dart's player0x position limited to 1 through 160.
When we divide in bB, we lose the remainder. (There's a way to keep the remainder, but in this case we don't care about it, anyway.) So what we want to end up with is the following:
player0x = 17 through 20 ... Column 0
Adjusted player0x position = 0 through 3 ... Divided by 4 ... Column 0
(i.e., 0/4 = 0, 1/4 = 0, 2/4 = 0, and 3/4 = 0)
player0x = 21 through 24 ... Column 1
Adjusted player0x position = 4 through 7 ... Divided by 4 ... Column 1
(i.e., 4/4 = 1, 5/4 = 1, 6/4 = 1, and 7/4 = 1)
player0x = 25 through 28 ... Column 2
Adjusted player0x position = 8 through 11 ... Divided by 4 ... Column 2
(i.e., 8/4 = 2, 9/4 = 2, 10/4 = 2, and 11/4 = 2)
etc.
Looking at that, we can see that we need to subtract 17 from player0x before we divide by 4. We don't want to change player0x (because we don't want the dart to move as a consequence of our computations), so we use a variable. Rather than use up one of the 26 user variables, we can use one or more temporary variables instead. Let's call them t1, t2, etc. (I like short variable names, unless there's a specific reason to use longer, more meaningful names-- and these are just temporary variables that we don't need to keep, so short names are fine.)
t1 = (player0x - 17) / 4
This line converts the player0x position into one of our grid column numbers, 0 through whatever. Of course, subtracting 17 from player0x could give us a negative number-- specifically, 0 - 17 = -16, through 16 - 17 = -1-- but since we're working with bytes, those negative numbers will be equivalent to 240 (256 - 16 = 240) through 255 (256 - 1 = 255), which will fall outside of the dartboard, anyway, so we don't need to worry about them.
However, we're simplifying the dartboard's grid by taking just the upper left quarter of it, so we can adjust the results-- in other words, we're taking the "reflection" of some of the columns. If t1 (the column number) is 5, then we want to use 3 instead. If t1 is 6, we want to use 2. If t1 is 7, we want to use 1. And if t1 is 8, we want to use 0:
x012345678x -- original column numbers
x012343210x -- reflected column numbers
00000000000
00011111000
00122222100
01223332210
01234443210
01234543210
01234443210
01223332210
00122222100
00011111000
00000000000
That means 8 - t1 gives us the reflected column number (8 - 5 = 3, 8 - 6 = 2, 8 - 7 = 1, and 8 - 8 = 0), but we want to do this only when t1 is greater than 4:
if t1 > 4 then t1 = 8 - t1
Finally, this is going to give us some more negative numbers (when t1 is greater than 8 ), but those will be values that fall outside of the dartboard, anyway-- and when we express the negative values as positive byte values, they'll always be greater than 4 (if we're going to restrict the dart's X position to 1 through 160). So rather than have a whole bunch of extra numbers where the dart has fallen outside of the dartboard, let's set all of those extra/unwanted values to 255:
if t1 > 4 then t1 = 255
In other words, if t1 = 255 after all of this, then we know the dart is in "column x," or "outside the dartboard."
Putting that all together, we have the following:
t1 = (player0x - 17) / 4
if t1 > 4 then t1 = 8 - t1
if t1 > 4 then t1 = 255
We can do exactly the same thing with the player0y position, as follows:
player0y = 0 through 8 ... Row x
player0y = 9 through 16 ... Row 0
player0y = 17 through 24 ... Row 1
player0y = 25 through 32 ... Row 2
player0y = 33 through 40 ... Row 3
player0y = 41 through 48 ... Row 4
player0y = 49 through 56 ... Row 5 (or row 3 after reflecting it)
player0y = 57 through 64 ... Row 6 (or row 2 after reflecting it)
player0y = 65 through 72 ... Row 7 (or row 1 after reflecting it)
player0y = 73 through 80 ... Row 8 (or row 0 after reflecting it)
player0y = 81 through 255 ... Row x
player0y = 9 through 16 ... Row 0
Adjusted player0y position = 0 through 7 ... Divided by 8 ... Row 0
(i.e., 0/8 = 0, 1/8 = 0, 2/8 = 0, 3/8 = 0, 4/8 = 0, 5/8 = 0, 6/8 = 0, and 7/8 = 0)
player0y = 17 through 24 ... Row 1
Adjusted player0y position = 8 through 15 ... Divided by 8 ... Row 1
player0y = 25 through 32 ... Row 2
Adjusted player0y position = 16 through 23 ... Divided by 8 ... Row 2
etc.
t2 = (player0y - 9) / 8
if t2 > 4 then t2 = 8 - t2
if t2 > 4 then t2 = 255
However, there's a slight wrinkle, because the player0y positions don't line up exactly with the borders of the playfield pixels. In particular, for player0y = 9, the tip of the dart is halfway in row x (above the dartboard) and halfway in row 0. Similarly, player0y = 17 is half on row 0 and half on row 1; player0y = 25 is half on row 1 and half on row 2; etc. As I mentioned in an earlier post, I'm assuming that in these cases, the dart will be considered to be in the higher-scoring circle, to give the player "the benefit of the doubt" as it were. That means each row will be 8 player0y positions tall-- except for the centermost row (the red bull's eye), which will be 9 player0y positions tall (because *both* of the "iffy" positions will be assigned to the bull's eye). That gives us the following:
player0y = 0 through 8 ... Row x
player0y = 9 through 16 ... Row 0
player0y = 17 through 24 ... Row 1
player0y = 25 through 32 ... Row 2
player0y = 33 through 40 ... Row 3
player0y = 41 through 49* ... Row 4
player0y = 50* through 57* ... Row 5 (or row 3 after reflecting it)
player0y = 58* through 65* ... Row 6 (or row 2 after reflecting it)
player0y = 66* through 73* ... Row 7 (or row 1 after reflecting it)
player0y = 74* through 81* ... Row 8 (or row 0 after reflecting it)
player0y = 82* through 255 ... Row x
In this slightly-adjusted list, "*" indicates a value that we "bumped up" by 1 so each borderline player0y position will always be associated with the higher-scoring circle on the dartboard. To "bump up" the values in this way, we can simply subtract 1 whenever player0y is greater than 48, as follows:
t2 = player0y - 9
if player0y > 48 then t2 = t2 - 1
The reason we subtract 1 like this is so the results will conform to the desired pattern (for computational purposes):
player0y = 0 through 8 ... Subtract 9 ... t2 = -9 through -1 ... Divide by 8 ... Row x
player0y = 9 through 16 ... Subtract 9 ... t2 = 0 through 7 ... Divide by 8 ... Row 0
player0y = 17 through 24 ... Subtract 9 ... t2 = 8 through 15 ... Divide by 8 ... Row 1
player0y = 25 through 32 ... Subtract 9 ... t2 = 16 through 23 ... Divide by 8 ... Row 2
player0y = 33 through 40 ... Subtract 9 ... t2 = 24 through 31 ... Divide by 8 ... Row 3
player0y = 41 through 48** ... Subtract 9 ... t2 = 32 through 39 ... Divide by 8 ... Row 4
player0y = 49** through 56** ... Subtract 9 ... t2 = 40 through 47 ... Divide by 8 ... Row 5
player0y = 57** through 64** ... Subtract 9 ... t2 = 48 through 55 ... Divide by 8 ... Row 6
player0y = 65** through 72** ... Subtract 9 ... t2 = 56 through 63 ... Divide by 8 ... Row 7
player0y = 73** through 80** ... Subtract 9 ... t2 = 64 through 71 ... Divide by 8 ... Row 8
player0y = 81** through 254** ... Subtract 9 ... t2 = 72 through 245 ... Divide by 8 ... Row x
In this list, "**" indicates where we subtracted 1 as described above. So putting all of that together, including where we reflect the rows and then assign 255 to the rows that are outside the dartboard, we get the following:
t2 = player0y - 9
if player0y > 48 then t2 = t2 - 1
t2 = t2 / 8
if t2 > 4 then t2 = 8 - t2
if t2 > 4 then t2 = 255
Now all we have to do is convert the t1 (column) and t2 (row) values into a single index, t3, which we'll use to read from our data table. Since the table is 5-by-5, we use t3 = 5 * t1 + t2. However, we don't want to do this when t1 or t2 is 255 (or outside the dartboard grid), so we exclude those cases:
if t1 < 5 && t2 < 5 then t3 = 5 * t1 + t2
The "< 5" works for us, because t1 and t2 are each going to be 0, 1, 2, 3, 4, or 255, so the only "legal" values will be less than 5. But we still want to know when the dart has landed outside the dartboard, so we'll assign all the "outside" positions to t3 = 25, as follows:
if t1 < 5 && t2 < 5 then t3 = 5 * t1 + t2 else t3 = 25
Then we just tack an extra byte onto the end of our data table, for when t3 = 25:
data points
0, 0, 1, 1, 1
0, 1, 2, 2, 2
1, 2, 2, 3, 3
1, 2, 3, 4, 4
1, 2, 3, 4, 5, 0
end
Now we can read the points from the table, and store them in a temporary variable. Rather than use a fourth temporary variable, I just reused t1, since it's already served its purpose as a column number and we don't need it for that anymore:
t1 = points[t3]
Then the only thing we need to do is decide how many points each of these is really worth. I just decided to use 10, 20, 30, 40, and 50, but you can make them whatever you want them to be. And you can put the actual point values into the data table, as follows:
data points
0, 0, 10, 10, 10
0, 10, 20, 20, 20
10, 20, 20, 30, 30
10, 20, 30, 40, 40
10, 20, 30, 40, 50, 0
end
Unfortunately, right now bB doesn't work right when you try to assign a variable to the score, so I initialized the score to 0, then used a for...next loop to increment the score based on the points-- and that works best when there aren't too many iterations of the loop, otherwise it can take too long and cause the screen to scroll, so I stuck with 1 through 5:
score = 0
if t1 > 0 then for t2 = 1 to t1 : score = score + 10 : next
rem * Or you could use score + 100, score + 50, etc.
This time, I reused t2 for the for...next loop counter, rather than wasting some other variable.
As far as the three temporary variables that I've used-- t1, t2, and t3-- we need to be careful in assigning them to bB's temporary variables, because the division and multiplication routines use temp1 and temp2. So I've assigned them to temp3, temp4, and temp5:
dim t1 = temp3
dim t2 = temp4
dim t3 = temp5
I hope you were able to follow my explanation. Now you can focus on the *really* hard part-- coming up with the code for the dart's velocity and trajectory!
Michael
Edited by SeaGtGruff, Wed Sep 17, 2008 10:23 PM.