Showing posts with label checkers game. Show all posts
Showing posts with label checkers game. Show all posts

Thursday, January 10, 2008

Building A Second Life Checkers Game #9

I've finished the tic-tac-toe game. It works and I've played it a couple of times with people. Probably a few more bugs and I could make it look a little nicer as well, but overall the goal is complete, I built a two player table top game. I'm trying to figure out if I'm going to go back and finish the checkers game. I don't think I will because there doesn't seem to be a lot of value in it. I would like to be more innovative than that and make a new game that is only available virtually. Something completely new. I started on and I'll have a screen shot of that tomorrow, but it doesn't have any mechanics besides something in my head. When making video games I typically just get a mechanic working then tweak it until some sort of game forms. So, after 9 posts I feel like I may have mislead you into thinking we would have a checkers game and here we end up with a lot less, except for the knowledge that it is possible and I have most of the functional code required to do it. Anyway, I'll keep digging until I have something more. If you want a copy of the tic-tac-toe game, send me and e-mail or find me in SL: Wood Wheels.


One thing I don't think that shows here is that the green triangles point in the direction of the player who's move it is. When the player makes a move I change the rotation of all the green triangles like this.

RotateBtns(integer xoro)
{
integer itmp;
integer btnnum=0;
integer btnlinknum;
integer pos = 0;
rotation rot;
if (xoro==1)
{
rot = rot0;
}
else
{
rot = rot180;
}

for (;pos<9;pos++)
{
itmp = llList2Integer(btnpositions,pos);
if (itmp>=0)
{
llSetLinkPrimitiveParams(itmp,[PRIM_ROTATION,rot]);
}
}

}

This may look pretty simple, but it has a trick. I originally tried to just rotate 180 around the z axis, but as I should have known before I started this would not work as rotations are cumulative and coming up with a rotation axis is not trivial piece of math. Quaternians are simply a vector associated with a rotation angle around that vector. They can be hard to calculate programatically.

The solution? Find the two rotation angles at build time and use them as static values.

vector eul = <0,77,0>; //0 degrees around the z-axis, in Euler form
eul *= DEG_TO_RAD; //convert to radians
rot0 = llEuler2Rot(eul); //convert to quaternion
eul = <3.9,283.0,184.45>; //180 degrees around the z-axis, in Euler form
eul *= DEG_TO_RAD; //convert to radians
rot180 = llEuler2Rot(eul); //convert to quaternion

Cheating I know, but most programming is about getting something to work, even though it might not be the most elegant solution. At least that is how I work. Why not the elegant solution you ask? One takes hours and hours and one just a few minutes and schedule is usually the most important element of all. Also, if it really needs to be elegant, it is better to get it working and come back in later if it really needs it.

Wednesday, January 9, 2008

Building A Second Life Checkers Game #8


Success. I finally got to the point where I could do some testing. Finding someone to help you test is incredibly hard. I have about 30 people listed as friends and of course no one is online. I start asking around at various sand boxes and no one will even lift a finger. Finally one of my friends logs on and it is the person who was harassing me just a while back. I ask him and he is more than eager to help.


So it really pays to try and make friends even when someone is being rude.

There were a couple of small problems, but overall it is working. The next step is to redo the "programmer art" and make it look a little more professional. I've been watching HGTV to try and decide on a color scheme for the game that will fit in most residences. Yeah right.

Tuesday, January 8, 2008

Building A Second Life Checkers Game #7

The checkers game, currently the tic-tac-toe game is coming along nicely. I made really great progress last night.


The pieces are placed in order as the green traingles are touched. The peices are stored in a box below the table and the triangles are swapped to that location when the triangle is touched. Next I'm starting to work on the seating and makeing the game play depended on two avatars seated on the "chairs". The little green button on the left resets the entire board.

Here is some code to move a piece to a specific location on the board.

MoveLinkToPos(integer iLinkNum, integer posWhere)
{
key rootKey = llGetLinkKey(1);
list params = llGetPrimitiveParams([PRIM_SIZE]);
vector sz = llList2Vector(params,0);

//llOwnerSay("area size = "+(string)sz);
integer px = posWhere / 3;
integer py = posWhere % 3;
//llOwnerSay(" px="+(string)px+" py="+(string)py);

// set the position of the piece to 0,0
float sqr_x_size = sz.x / 3;
float sqr_y_size = sz.y / 3;
//llOwnerSay(" sqr_x_size="+(string)sqr_x_size+" sqr_y_size="+(string)sqr_y_size);
vector p = < (sqr_x_size * px) - (sz.x/2) + (sqr_x_size/2),
(sqr_y_size * py) - (sz.y/2) + (sqr_y_size/2),
0.051 >;
//llOwnerSay(" new pos = "+(string)p);
llSetLinkPrimitiveParams(iLinkNum,[PRIM_POSITION,p]);
}


Most of this code was outlined in post #4. I just consolidated it into a function.

And some code to move a piece into the storage box.

MoveLinkToStorage(integer iLinkNum)
{
llSetLinkPrimitiveParams(iLinkNum,[PRIM_POSITION,vStorage]);
}


When the game starts up I store the location of all the buttons.

 for (i=2;i<=iNumPrims+1;i++)
{
string stName = llGetLinkName(i);
key keyObj = llGetLinkKey(i);

list a = llGetObjectDetails(keyObj,([OBJECT_POS]));
vector p = llList2Vector(a,0) - regionPos;

if (stName=="movebtn")
{
btnlinks = llListInsertList(btnlinks,[i],0);
}
else if (stName=="xtile") ....
}
ShowAllBtns();


Here is the ShowAllBtns function.

ShowAllBtns()
{
integer itmp;
integer btnnum=0;
integer btnlinknum;
integer pos = 0;
vector v;
for (;pos<9;pos++)
{
itmp = llList2Integer(board,pos);
if (itmp==-1)
{
btnlinknum = llList2Integer(btnlinks,btnnum);
//llOwnerSay("move linkbtn="+(string)btnlinknum+" to pos="+(string)pos);
MoveLinkToPos(btnlinknum,pos);
btnnum++;
}
else
{
btnlinknum = -1;

}
//llOwnerSay(" adding btnlinknum="+(string)btnlinknum+" at pos="+(string)pos);
btnpositions = llListReplaceList(btnpositions,[btnlinknum],pos,pos);
}
//llOwnerSay("btnpositions="+(string)btnpositions);

// move the rest of the buttons to the storage
for (;btnnum<9;btnnum++)
{
btnlinknum = llList2Integer(btnlinks,btnnum);
MoveLineToStorage(btnlinknum);
}
}


I'm going through all the board positions looking for a -1 meaning that the location doesn't already have a piece. If it doesn't, I move the first (then next) button to that location and save which button is at that point in the btnpositions list. At the end I move the rest of the buttons that were not used to storage so they are not visible. Using this list, when a button is touched we can convert the link number given by the touch event into a position on the board by traversing the btnpositions list.

integer FindBtnPos(integer iLinkNum)
{
integer count = llGetListLength(btnpositions);
integer itmp;
integer i=0;
for (;i<9;i++)
{
itmp = llList2Integer(btnpositions,i);
//llOwnerSay("btn at pos "+(string)i+" = "+(string)itmp);
if (itmp==iLinkNum)
{
return i;
}
}
return -1;
}

Monday, January 7, 2008

Building A Second Life Checkers Game #6

Detour number 6 is what this post should really be called. After the experiment for positioning the pieces programatically instead of based on the initial layout of the pieces I decided to take a step back and finish something simpler first. I took that sample board and turned it into the first draft of a tic-tac-toe game.



The green triangles will be the location to click on to place the actual piece during play. The texture for the board is the same as the checker board, a 2x2 grid repeated the correct number of times. In the case of checkers it was repeated 4 times in the x and y direction. In the case of tic-tac-toe it is repeated 1.5 times in each direction then I had to offset the u and v direction by 0.75. I'm not smart, I just fiddled with it until it worked.

That said, I think this step back will actually be a step forward since I'll be able to do most of the work for the checkers game in a scaled down fashion, then take that knowledge to the larger game.

Monday, December 17, 2007

Building A Second Life Checkers Game #4

I was making good progress on the checkers game, but something was nagging at me in the design and I decided to take a step back and see if there might be a better way. In the current design, I've been saving the initial placement of all the pieces (as a list of vectors) on initialization and using those locations when people move or choose the location to move a piece. I had been avoiding calculating the locations programatically for no other reason than it seemed like it might take more thought (ie. lazy). But then there was the nag in my head that said it needed to be investigated, so I took the board apart and made a new one just to test how difficult it would be to programatically calculate the locations on the board.

Here is a screenshot of my mockup environment.

The positions would be numbered 0-63. The starting location would be 0. Touch the green button and the piece would move to the next position.

The first problem is to find the size of the board. Since it is the root object it's pretty easy to find. To note, if it's not the root prim you are screwed and it is very difficult, and I'll talk about that in the next post. The size (PRIM_SIZE) is the actual size of the object in meters. When calculating the location on that prim with local coordinates, the origin is at the center of the prim.

list params = llGetPrimitiveParams([PRIM_SIZE]);
vector sz = llList2Vector(params,0);


Next is to calculate the x and y row and column. This is also very simple given there are 8 rows and 8 columns.

integer px = curPos / 8;
integer py = curPos % 8;


I then calculate the size of each square and save it.

float sqr_x_size = sz.x / 8;
float sqr_y_size = sz.y / 8;


Once you have all that it is fairly straight forward to calculate the location of that square, but look at all the comments and see how many tests I went through to get it right. Tons of experimentation here.


//llOwnerSay(" sqr_x_size="+(string)sqr_x_size+" sqr_y_size="+(string)sqr_y_size);
vector p = < (sqr_x_size * px) - (sz.x/2) + (sqr_x_size/2),
(sqr_y_size * py) - (sz.y/2) + (sqr_y_size/2),
0.051 >;

//vector p = < (sqr_x_size * px) - (sz.x/2),
// (sqr_y_size * py) - (sz.y/2),
// 0.1 >; // the position without centering on the square
//vector p = < sz.x/2, sz.y/2, 0.1 >; // the origin
//vector p = < -sz.x/2, -sz.y/2, 0.1 >; // the negative origin - this didn't work
//vector p = <0,0,0.1>; // the 0,0 location which is the center of the board
//vector p = <(sqr_x_size/2),(sqr_y_size/2),0.1>; // just a futile experiement to offset from the origin some amount
//llOwnerSay(" new pos = "+(string)p); // the debug message seen over and over and over again

Given all that, the actual calculation that I used can be summed up like this.
For x: The location given the x location minus the offset because the origin is the center + the offset of half a square to center on the square.
For y: Same as x
For z: Just a number which I twiddled to get right.

The last step is to set the actual location. This is pretty simple given that you know the link num for the piece you want to move. This number was retrieved on initialization. After that, change curPos and wait for the next touch.

llSetLinkPrimitiveParams(pieceLinkNum,[PRIM_POSITION,p]);
curPos++;


And yes, this was more work, but as I expected it will be a lot more efficient and easier to work with in the long run.

Friday, December 14, 2007

Building a SecondLife checkers game #3

Serious design problems.

My checkers game is now receiving a:
Script run-time error
Stack-Heap Collision

I know what the problems are and I know how to fix it, but it does show how flexible software development needs to be. I knew going in that memory constraints on the scripts were going to be tight, I just didn't figure I would hit them this quick. Here is the latest screen shot.

The good news is that I know what the problem is and I may not need to change the design too much.

The green triangles are not visible during game play except to choose the location where you can move a piece. The mechanic is to touch the piece you want to move, then the green triangles appear in locations that are open, then you click on the triangle. I've got all the movement and placement of the triangles working.

The problem occurred when I created enough extra triangles for the number of moves that might be available. For the pieces I'm saving the original location vectors (three floats) in a list. Including the ones in the center which will become the king pieces. Then removing the kings from the board (you can see the storage box in the back which will be moved under the table at the end. These vectors are big and I was also saving the triangles just for testing and that is where I hit the problem. I can really just keep them off the table at all times and don't need their initial locations. So, even though I hit what seems like a big snag, all is not lost yet... And even if it were, there is always a work around. Always.

Thursday, December 13, 2007

Building A Second Life Checkers Game #2

I'm making progress on the checkers game and thought I would talk about some of the data structures.

The available LSL data structures are quite limited, but enough to make scripts that do anything.

The biggest thing you learn early on is that there are no arrays. At least not like in other languages. What they do have are lists which are basically arrays, but you have to use special functions to manage the arrays.

As an example, I know my board has 32 spaces so I have a list that contains the piece number at any given space. You first have to allocate space for the 32 spaces.

list board;

InitializeBoard()
{
integer i=0;
for(;i<32;i++)
{
llListInsertList(board, [-1], 0); // -1 means no piece is there
}
}

As I traverse through the linked pieces (see previous entry), I insert them into the board at their location which is defined with a little CSV in their title "r,1,1". The first letter is the color, the second is the piece number and the third is the location which are the same. I may have over designed the piece number and location...

Once you know the piece number you can add it to the data structure (list) holding the locations on the board.

board = llListReplaceList(board,[piece_num],piece_location,piece_location);

One thing to note, ReplaceList creates a whole new list and returns it. You WILL at some point screw this up and not have the board = in there and wonder why your list update didn't occur. It is just part of the learning curve. I'm sure I'll do it again and again and waste more hours. I think the compiler should warn you of this since it seems so necessary and such a common mistake.

When you want to access an element in the list you do so knowing the type that you previously stored in the list. In this case it was an integer and if I want to know what was at location 3, I would do this.
integer pieceAtLocation = llList2Integer(board,3);

For other types stored in lists, there are other functions, llList2String, llList2Vector, llList2Key, etc...

Wednesday, December 12, 2007

Distilling a Second Life harassment incident

When building my checkers game I've had a number of people approach and touch the board and at least two attempts to disrupt and divert my attention. On both I asked them nicely to stop and reported the incident as harassment when they didn't. I sent an IM to both people. The second person (who had built a lot of large cubes over the area I was working and made me move, he also used a lot of expletives) became irate and came back upset when I told him I had reported the harassment. I didn't responded to any of the verbal attacks and I was finally able to distill the incident by saying something like this.

Me: Hey, how about we work together on something together instead being disruptive. I find that life is a lot more fun when we help each other and work together.
Me: I think it would be interesting to work on a project with you. I have a lot of programming experience and help on the scripting side of a build.
Mr. Disruptive: You mean a truce?

(I never knew I was at war)

Me: Yeah, sure.
Mr. Disruptive: Okay, cool.

He had left the area and then asked me for a teleport. He never really talked to me again, but he hung out in the area tweaking a spheroid and soon added me as a friend. So as I've found in life and now in virtual life, bad situations can usually be distilled by an offer to work together even when one person is bent on being disruptive. The charity work I do is the most rewarding thing that I have ever done and I'm always looking for ways to continue those efforts. It seems there is even room for this type of work in the virtual world!

I wrote this up a few days ago and he actually came back around to show me some new emotes he had received from a friend. I think he's just a kid, but its much better than the initial interactions.

Tuesday, December 11, 2007

Building a Second Life Checkers Game

I (SL: Wood Wheels) have been working on a checkers game in Second Life. This is just a learning experience and a chance to really dig into a new language. I've found that the best way to learn a new language is to build something and keep increasing the complexity. I've built a bunch of houses, doors, lamps and a soccer ball, but this is definitely the next step in the evolution of my understanding.
There are a bunch of lessons learned so far and I thought this would be a good opportunity to pass some of those on beyond a couple of updates to the lslwiki and slwiki.

The first lesson was where I started out giving each game piece it's own script and sending messages back to a larger script attached to the board. This was using Link Messages (llMessageLinked). It was good that I figured out early that this was the wrong way after reading the section on Script Effeciency. #1 in efficient design: If you need to have a bunch of buttons, don't put a script in each prim, use llDetectedLink number.

I now have only one script in the board. By default, each linked prim has a link number and you can traverse the list of linked prims using something like this:


integer count = llGetNumberOfPrims();
integer i = 2;
for (;i<=count;++i)
{
string stName = llGetLinkName(i);
list namelist = llCSV2List(stName);
string stType = llLink2String(0);
string stPieceNum = llLink2String(1);
if ((stType=="r") || (stType=="b"))
{
llOwnerSay("have a "+stType+" piece numbered "+(string)stPieceNum);

// I also save the link number, color and piece number
// in a list which I'll show later
}
}

I gave each piece on the board an object name of "r,1" or "r,2" which allows me to identify which piece is touched during the move. I then convert this name to a list (llCSV2List()) to separate out the color and piece number. You might note that i started with 2. The board, which is the root prim (the one selected last when linking), is number 1 and I don't include it in the lookup. I don't have the move mechanic completed, but the early design of it is working and I'm refining the movement code now.

There is probably a lot more to talk about and lists are a big part of the design. I'll try and write more on that tomorrow.