📄 756-759.html
字号:
<HTML>
<HEAD>
<META name=vsisbn content="1571690433"><META name=vstitle content="Black Art of Java Game Programming"><META name=vsauthor content="Joel Fan"><META name=vsimprint content="Sams"><META name=vspublisher content="Macmillan Computer Publishing"><META name=vspubdate content="11/01/96"><META name=vscategory content="Web and Software Development: Programming, Scripting, and Markup Languages: Java"><TITLE>Black Art of Java Game Programming:The Internet MahJong Server</TITLE>
<!-- HEADER --><STYLE type="text/css"> <!-- A:hover { color : Red; } --></STYLE><META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW"><script><!--function displayWindow(url, width, height) { var Win = window.open(url,"displayWindow",'width=' + width +',height=' + height + ',resizable=1,scrollbars=yes'); if (Win) { Win.focus(); }}//--></script><SCRIPT><!--function popUp(url) { var Win = window.open(url,"displayWindow",'width=400,height=300,resizable=1,scrollbars=yes'); if (Win) { Win.focus(); }}//--></SCRIPT><script language="JavaScript1.2"><!--function checkForQuery(fm) { /* get the query value */ var i = escape(fm.query.value); if (i == "") { alert('Please enter a search word or phrase'); return false; } /* query is blank, dont run the .jsp file */ else return true; /* execute the .jsp file */}//--></script></HEAD><BODY>
<TABLE border=0 cellspacing=0 cellpadding=0>
<tr>
<td width=75 valign=top>
<img src="../1571690433.gif" width=60 height=73 alt="Black Art of Java Game Programming" border="1">
</td>
<td align="left">
<font face="arial, helvetica" size="-1" color="#336633"><b>Black Art of Java Game Programming</b></font>
<br>
<font face="arial, helvetica" size="-1"><i>by Joel Fan</i>
<br>
Sams, Macmillan Computer Publishing
<br>
<b>ISBN:</b> 1571690433<b> Pub Date:</b> 11/01/96</font>
</td>
</tr>
</table>
<P>
<!--ISBN=1571690433//-->
<!--TITLE=Black Art of Java Game Programming//-->
<!--AUTHOR=Joel Fan//-->
<!--AUTHOR=Eric Ries//-->
<!--AUTHOR=Calin Tenitchi//-->
<!--PUBLISHER=Macmillan Computer Publishing//-->
<!--IMPRINT=Sams//-->
<!--CHAPTER=18//-->
<!--PAGES=756-759//-->
<!--UNASSIGNED1//-->
<!--UNASSIGNED2//-->
<CENTER>
<TABLE BORDER>
<TR>
<TD><A HREF="752-756.html">Previous</A></TD>
<TD><A HREF="../ewtoc.html">Table of Contents</A></TD>
<TD><A HREF="759-762.html">Next</A></TD>
</TR>
</TABLE>
</CENTER>
<P><BR></P>
<P>This method is declared with a <I>synchronized</I> keyword, because it is potentially called by multiple players’ threads. Now, you might recall from previous discussion that all packet handlers, hence play(), must return quickly. Fortunately, for most board and card games this is not a problem—the rules are simple and easy to check.</P>
<P>So what is involved in the play() method? Let’s just take one case, where a Player <I>him</I> makes a discard. First check whether he has that tile:</P>
<!-- CODE //-->
<PRE>
if (action == DISCARD) { // he wants to discard
int s = him.seat;
pri_hands[s].unselectAll(); // unmark everything
if (pri_hands[s].search(tile, 1) == 0) {
him.output(Packet.SPYourPlay(false)); // reject this play
return;
}
him.output(Packet.SPYourPlay(true)); // confirm play
pri_hands[s].delete(); // delete from hand
active = tile; // make the discard
outputTable(Packet.SPDiscard(tile)); // tell everyone
</PRE>
<!-- END CODE //-->
<P>Then, tell everyone about the discarded tile, and what this player’s hand looks like now:
</P>
<!-- CODE //-->
<PRE>
// conceal the concealed tiles to the other players
Hand to_other = new Hand();
to_other.append(pub_hands[s]);
for (int i = 0; i < semi_pubs[s]; i++) {
to_other.addTile((byte)0, semi_pubs[s].groupAt(i), false);
}
for (int i = 0; i < pri_hands[s]; i++) {
to_other.addTile((byte)0, Hand.SINGLE, false);
}
for (int i = 0; i < 4; i++) { // send to others
if (players[i] != null && i != s)
players[i].output(Packet.SPTiles(s, to_other));
}
</PRE>
<!-- END CODE //-->
<P>We also have to determine what the other players can do with the discarded tile, and tell them that:
</P>
<!-- CODE //-->
<PRE>
for (int i = 0; i < 4; i++) {
if (i == s) { // he can only wait
actions[s] = PASS;
action_masks[s] = 0;
him.output(Packet.SPYourChoices(0));
continue;
}
actions[i] = -1;
Hand h = pri_hands[i];
if (i == (s + 1) % 4) { // immediate right
action_masks[i] = 1 << DRAW; // he can draw
boolean up1 = h.search(Tile.stepUp(tile), 1) != 0;
boolean up2 = h.search(Tile.stepUp(Tile.stepUp(tile)), 1) != 0;
boolean dn1 = h.search(Tile.stepDn(tile), 1) != 0;
boolean dn2 = h.search(Tile.stepDn(Tile.stepDn(tile)), 1) != 0;
if (up1 && up2)
action_masks[i] |= 1 << CONN_UP; // he can chow
if (up1 && dn1)
action_masks[i] |= 1 << CONN_MD; // he can chow
if (dn1 && dn2)
action_masks[i] |= 1 << CONN_DN; // he can chow
} else action_masks[i] = 1 << PASS; // he can pass (not draw)
int n_id = h.search(tile, 3);
if (n_id >= 2) {
action_masks[i] |= 1 << TRIP; // he can pong
if (n_id == 3)
action_masks[i] |= 1 << QUAD; // he can gong
}
h.unselectAll(); // unmark everything again
if ((winning[i] = CheckMahJong(s, tile)) != null)
action_masks[i] |= 1 << WIN; // he can win
if (players[i] != null) // tell him his choices
players[i].output(Packet.SPYourChoices(action_masks[i]));
}
</PRE>
<!-- END CODE //-->
<P>The other cases are quite similar to this, just tedious to write down fully. If you are writing your own game, be sure to produce something like Figure 18-5 first. Then it’s just a matter of translating it into code.
</P>
<H4 ALIGN="LEFT"><A NAME="Heading35"></A><FONT COLOR="#000077">Checking for a MahJong Pattern</FONT></H4>
<P>The CheckMahJong() method we call above looks at a player’s hand (revealed tiles together with concealed ones) and determines if he has a MahJong pattern. If so, the method returns a Hand with all the tiles grouped up into four sets and a pair. Otherwise, it returns <I>null</I>. This can be achieved with a simple recursive algorithm.</P>
<P>The recursive function takes as arguments a fixed hand of tiles that are already grouped up, a boolean value indicating whether the fixed hand contains a pair or not, and another hand of tiles to test on. We simply look at the first tile of the test hand, and see if we can make chows, a pong, or a pair (if the fixed hand doesn’t have one already) with it and other tiles in the test hand. If no groups can be formed, we return <I>null</I>. Otherwise, in each possible case, we construct a new set of arguments using the new group we form, call the function itself again, and return the first non-<I>null</I> return value from the recursive calls. In the final situation, we have an empty test hand, which means the fixed hand is a MahJong pattern, which is returned all the way up the calling stack. The implementation is tedious yet straightforward.</P>
<P>Given this recursive function, our CheckMahJong() method simply calls it with the fixed hand being the revealed tiles plus concealed gongs, which also have fixed status, the boolean value being <I>false</I>, and the test hand being the concealed tiles.</P>
<H3><A NAME="Heading36"></A><FONT COLOR="#000077">Adding Finishing Touches</FONT></H3>
<P>After the first release of my software, I kept adding features to it. They are not fancy, but either make the server a more robust environment or increase players’ convenience. I will discuss them briefly here.
</P>
<H4 ALIGN="LEFT"><A NAME="Heading37"></A><FONT COLOR="#000077">A Flexible Scoring System</FONT></H4>
<P>No game will attract players unless it calculates score. For MahJong, there are well-defined scoring systems, but alas, too many of them. I have chosen a large common subset of the most popular conventions, and allow some minor variations that are adjustable as table options. Implementing them is relatively straightforward after what we’ve been through. It’s just a matter of adding variables to the Table class, patching up the protocol to transmit the options, and finally, making a new GUI window for the players to view and change them. To prevent chaos, I only allow the creator of a table to change the options, and only when no game is in progress.
</P>
<H4 ALIGN="LEFT"><A NAME="Heading38"></A><FONT COLOR="#000077">A Player Database</FONT></H4>
<P>The database records a player’s name, password, score, and optional data such as e-mail address, home page URL, a “plan,” and his preferred client setup. Since I did not own a database server, I had to write my own. What I came up with is the RecordFile class, based on the library RandomAccessFile. Abstractly, a RecordFile holds an arbitrary number of <I>records</I>, which are simply byte streams whose length can vary. The physical file is chopped into <I>blocks</I> of fixed length, and a record can occupy any number of blocks. As records shrink or grow, some blocks may be freed and reused later. Thus, a RecordFile can be thought of as an extremely simple file system! I associate to each player such a record, and use DataOutput and DataInput calls to store and retrieve player records.</P>
<P>The RecordFile class is in the types package. You may find it useful for your own games.</P><P><BR></P>
<CENTER>
<TABLE BORDER>
<TR>
<TD><A HREF="752-756.html">Previous</A></TD>
<TD><A HREF="../ewtoc.html">Table of Contents</A></TD>
<TD><A HREF="759-762.html">Next</A></TD>
</TR>
</TABLE>
</CENTER>
</BODY>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -