import java.awt.*; import java.awt.event.*; import java.applet.*; import java.util.*; public class Shixl extends Applet implements ActionListener, Runnable { public static final int num = 12;//number for determining size of board public static Frame f = new Frame(); static int width = 720, height = 600; int x = width/num, y = height/num; int w = width - x, h = height - y; int messagew = 200, messageh = 200;//size of popup messages int pnum/* number of players*/, rollnum/*number rolled*/, rollcount/*amount player has to move*/, pL/*players left in the game*/, pC/*current player*/, cC/*current fate card*/, tradeplayer/*player being offered a trade*/, trademoney/*money being traded*/, lotto/*money in lottery*/; boolean going/*currently moving*/, buying/*buying properties*/, trading1/*in first mode of trade*/, trading2/*in second trade mode*/, moneying/*adding money to trade*/, improving, unimproving; Message yesnomess, okaymess, selectmess, propmess, moneymess, hellmess; Thread t; Player[] p; Square[] sq; int[] cd;//fate cards Square curSq;//current square Dialog dial;//default dialog PB[] boardbtn;//buttons on the board LinkedList[] properties; LinkedList tradeoff/*items offered for trade*/, tradereq/*items requested in trade*/, boardmsg/*messages on board*/; //double buffering private Image dbImage; private Graphics dbg; public void init() { t = new Thread(this); t.start(); try { pnum = param("players"); }//get number of players from HTML catch (Exception e) { pnum = 4; } //define players and players' properties p = new Player[pnum]; properties = new LinkedList[pnum]; p[0] = new Player(width/(num-2), height/(num-2), new Color(0,0,175)); properties[0] = new LinkedList(); p[1] = new Player(p[0].x, p[0].y+15, new Color(255,25,0)); properties[1] = new LinkedList(); if (pnum > 2) { p[2] = new Player (p[0].x+20, p[0].y, new Color(0,255,25)); properties[2] = new LinkedList(); } if (pnum == 4) { p[3] = new Player (p[2].x, p[1].y, new Color(255,230,50)); properties[3] = new LinkedList(); } makesquares(); makemessages(); makecards(); pL = pnum; lotto = 100; pC = cC = rollnum = trademoney = 0; going = buying = trading1 = trading2 = improving = moneying = false; curSq = sq[0]; String[] boardbtnnames = {"Roll", "Trade", "Improve", "Unimprove"}; int boardbtnsize = 70; boardbtn = new PB[4]; setLayout(null); for (int i = 0; i < 4; i++) { boardbtn[i] = new PB(boardbtnnames[i], width/4+(i*boardbtnsize), height-height/4, this); boardbtn[i].setBounds(boardbtn[i].x, boardbtn[i].y, boardbtnsize,25); add(boardbtn[i]); } setBackground(Color.gray); tradeplayer = -1; boardmsg = new LinkedList(); for (int i = 0; i < 6; i++) boardmsg.add(""); } //gets parameters from HTML public int param(String p) { return new Integer(getParameter(p)).intValue(); } void makemessages() { LinkedList buttons = new LinkedList(); //general yes or no message buttons.add((Object)new PB("Yes", messagew/2-50, messageh/2-25, this)); buttons.add((Object)new PB("No",messagew/2,messageh/2-25, this)); yesnomess = new Message(this, messagew, messageh, buttons); //property selecting message buttons.clear(); buttons.add((Object)new PB("Done", messagew/2-50, messageh/2, this)); buttons.add((Object)new PB("Cancel",messagew/2,messageh/2, this)); selectmess = new Message(this, messagew, messageh, buttons); //message for choosing money amounts during trade buttons.add(new PB("$100", messagew/2-75, messageh/2-50, this)); buttons.add(new PB("$50", messagew/2-25, messageh/2-50, this)); buttons.add(new PB("$25", messagew/2+25, messageh/2-50, this)); buttons.add(new PB("$10", messagew/2-75, messageh/2-25, this)); buttons.add(new PB("$5", messagew/2-25, messageh/2-25, this)); buttons.add(new PB("$1", messagew/2+25, messageh/2-25, this)); moneymess = new Message(this, messagew, messageh, buttons); //general message buttons.clear(); buttons.add((Object)new PB("OK", messagew/2-25, messageh/2-50, this)); okaymess = new Message(this, messagew, messageh, buttons); //message for random guess while in hell buttons.clear(); buttons.add(new PB("1", messagew/2-75, messageh/2, this)); buttons.add(new PB("2", messagew/2-25, messageh/2, this)); buttons.add(new PB("3", messagew/2+25, messageh/2, this)); hellmess = new Message(this, messagew, messageh, buttons); } void makesquares() { String names[] = { "Start", "A1", "A2", "A3", "TAX", "B1", "B2", "B3", "HELL", "FATE", "C1", "C2", "C3", "SLIPPERY", "D1", "D2", "D3", "FATE", "HELL", "E1", "E2", "E3", "LOTTO", "F1", "F2", "F3", "STICKY", "FATE", "G1", "G2", "G3", "HELL", "H1", "H2", "H3", "FATE" }; boolean ownable[] = new boolean[36]; //if the square name contains a number, it's a property for (int i = 0; i < 36; i++) ownable[i] = names[i].contains("1") || names[i].contains("2") || names[i].contains("3"); sq = new Square[36]; int price = 5; for (int i = 0; i < 36; i++) { sq[i] = new Square(names[i], i); int thisprice = 0; if (!ownable[i]) { sq[i].owned = 4; sq[i].c = Color.black;//color set to black if not a property } else { sq[i].setPrice(price++); //define where nearby properties of the same type are if (ownable[i-1]) sq[i].neighbors[0] = i-1; else sq[i].neighbors[0] = i+2; if (ownable[i+1]) sq[i].neighbors[1] = i+1; else sq[i].neighbors[1] = i-2; } if (i < 10) {//top row sq[i].x = (i+1)*60; sq[i].y = 50; } else if (i < 19) {//right column sq[i].x = 600; sq[i].y = (i-10)*50 + 100; } else if (i < 28) {//bottom row sq[i].x = 600 - ((i-18) * 60); sq[i].y = 500; } else {//left column sq[i].x = 60; sq[i].y = 500 - ((i-27) * 50); } } } void makecards() { cd = new int[20]; int values[] = { -100, 18, 34, 50, 28, 1, -200, 7, 11, 250, 14, 18, 150, 21, -250, 100, 200, 24, -150, -50}; for (int i = 0; i < 20; i++) cd[i] = 0; for (int i = 0; i < 20; i++) { int index; while (cd[index = (int)(Math.random()*20)] != 0);//set index to random location, unless it's already been used cd[index] = values[i]; } } //checks to see which properties can be improved or unimproved LinkedList checkimprove(boolean un) { LinkedList checker = (LinkedList)properties[pC].clone(); LinkedList improvers = new LinkedList(); while (!checker.isEmpty()) { int p1 = new Integer(checker.remove().toString()).intValue(); int p2 = (sq[sq[p1].neighbors[0]]).num, p3 = (sq[sq[p1].neighbors[1]]).num; if (checker.contains(p2) && checker.contains(p3)) { if (imp(un, sq[p1].improvement)) improvers.add(p1); if (imp(un, sq[p2].improvement)) improvers.add(p2); checker.remove((Object)p2); if (imp(un, sq[p3].improvement)) improvers.add(p3); checker.remove((Object)p3); } } return improvers; } //checks whether selected properties can be improved or unimproved boolean imp(boolean un, int improvement) { if (un) return improvement > 0;//if unimproving, property needs some improvements else return improvement != 5;//if improving, property needs less than 5 } //shows properties for improving/unimproving/trading void propmessage (LinkedList props, String message) { props = sort((LinkedList)props.clone()); LinkedList btn = new LinkedList(); if (improving || unimproving) btn.add(new PB("Done", messagew/2-25, messageh/2-50, this)); else { btn.add(new PB("Done", messagew/2-50, messageh/2-50, this)); btn.add(new PB("Cancel", messagew/2, messageh/2-50, this)); } int i = 0; while(!props.isEmpty()) { int index = new Integer(props.remove().toString()).intValue(); Square s = sq[index]; if (improving || unimproving || s.improvement == 0) { int x = messagew/2-75+((i%3)*50); int y = messageh/2-10+(i/3)*35; PB addbutton = new PB(s.name, x,y, this); addbutton.setName(""+s.num); btn.add(addbutton); i++; } } if (!improving && !unimproving) { PB addbutton = new PB("$", messagew/2-75+((i%3)*50), messageh/2-10+(i/3)*35, this); addbutton.setName("$"); btn.add(addbutton); } propmess = new Message (this, messagew, messageh+((props.size()/3)*50), btn); alert(propmess, message); } //figures out what the tax is (10% of total assets) int getTax() { int assets = p[pC].money; if (assets < 0) assets = 0; LinkedList props = (LinkedList)properties[pC].clone(); while (!props.isEmpty()) { Square s = sq[(Integer)props.remove()]; assets += s.price; for (int i = 0; i < s.improvement; i++) assets += s.price/2; } return assets/10; } public void run() { boolean moving = false; while (true) { if (going) {//token is moving //move token if (p[pC].Pos < num-3) p[pC].x += x; else if (p[pC].Pos < (num-3)*2) p[pC].y += y; else if (p[pC].Pos < (num-3)*3) p[pC].x -= x; else p[pC].y -= y; p[pC].Pos++; //return to start if (p[pC].Pos == (num-3)*4) { p[pC].Pos = 0; p[pC].money += 100; } curSq = sq[p[pC].Pos]; repaint(); //pause briefly int sleeper = 200; if (moving) sleeper = 100; try { t.sleep(sleeper); } catch (Exception Maria) { System.out.println(Maria); } rollcount--; if (rollcount == 0) {//done moving going = false; moving = false; int own = curSq.owned; if (own == -1) {//property unowned; ask if they want it buying = true; alert(yesnomess, "Buy "+curSq.name+" for $"+curSq.price+"?"); } else if (own == 4) {//not a property; find out what it is String playername = "Player " + (pC+1) + " "; switch (curSq.num) { case 4://tax int tax = getTax(); p[pC].money -= tax; lotto += tax; addboardmessage("pays 10% tax ($"+tax+")"); break; case 22://lotto p[pC].money += lotto; addboardmessage("wins $" + lotto + " in the lottery!"); lotto = 100; break; case 13://turn addboardmessage("gains a turn."); break; case 8: case 31://go to hell! going = true; moving = true; try { t.sleep(500); } catch (Exception e) {} if (curSq.num == 8) rollcount = 10; else rollcount = 23;//send to hell square break; case 18: hell(); break; case 9: case 17: case 27: case 35://fate String fatemessage; if (cd[cC] > 0 && cd[cC] < 50) {//move to a different square going = true; moving = true; fatemessage = "Go to "+sq[cd[cC]].name; rollcount = (cd[cC]-p[pC].Pos)%36; if (rollcount < 0) rollcount += 36; if (cd[cC] == 18) { fatemessage += "!"; } else addboardmessage("moves to "+ sq[cd[cC]].name); } else {//lose or gain money if (cd[cC] < 0) { addboardmessage("loses $"+cd[cC]*-1); fatemessage = "Lose $"+cd[cC]*-1; } else { addboardmessage ("gains $"+cd[cC]); fatemessage = "Gain $"+cd[cC]; } p[pC].money += cd[cC]; } alert(okaymess, "Shix'l says: "+fatemessage); cC++; if (cC == 20) cC = 0; break; case 26://sticky addboardmessage("loses a turn."); p[pC].stuck = true; break; } if (!moving && curSq.num != 13) turn();//change turn unless we're on "slippery" square } else {//property is owned if (pC != own) {//if current player doesn't own it, charge rent int dollars = curSq.rent; System.out.println("paying"); addboardmessage("pays player " + (own+1) + " $"+dollars); p[pC].money -= dollars; p[own].money += dollars; } turn(); } repaint(); } } } } public void paint(Graphics g) { g.setColor(Color.gray); g.fillRect(0,0,width,height); g.setFont(new Font("SansSerif",Font.BOLD, 12)); //show squares and their names for (int i = 0; i < 36; i++) { g.setColor(sq[i].c); g.fillRect(sq[i].x, sq[i].y, 59, 49); g.setColor(Color.gray); g.drawString(sq[i].curname, sq[i].x+1, sq[i].y+47); if (i == 8 || i == 31) g.drawString("GO TO", sq[i].x+1, sq[i].y+37); else if (i == 22) g.drawString("$"+lotto, sq[i].x+1, sq[i].y+17); } g.setFont(new Font("SansSerif",Font.BOLD,14)); g.setColor(Color.black); if (going) { g.drawString("Roll: "+rollnum,x+width/4,height/3);//display squares left to move } g.setColor(p[pC].c); g.drawString("Player "+(pC+1)+"'s turn!", x+width/4,height/4); //show player tokens for (int i = 0; i < pnum; i++) { if (p[pC].playing) { g.setColor(Color.gray); g.fillOval(p[i].x,p[i].y,13,13); g.setColor(p[i].c); g.fillRoundRect(p[i].x+2,p[i].y+2,9,9,10,5); g.drawString( "Player " + (i+1) + " has $"+p[i].money+" and is on square "+sq[p[i].Pos].name, width/4, (i*14)+height/2 ); } } g.setColor(Color.black); g.fillRect(width/4, 350, 250, 75); g.setColor(Color.gray); g.setFont(new Font("Sans",Font.BOLD,12)); for (int i = 0; i < boardmsg.size(); i++) { g.drawString(boardmsg.get(i).toString(),width/4+1, 360+(12*i));//show board messages } } public void update (Graphics g) { // initialize buffer if (dbImage == null) { dbImage = createImage(this.getSize().width, this.getSize().height); dbg = dbImage.getGraphics(); } // clear screen in background dbg.setColor(getBackground()); dbg.fillRect(0, 0, this.getSize().width, this.getSize().height); // draw elements in background dbg.setColor(getForeground()); paint(dbg); // draw image on the screen g.drawImage(dbImage, 0, 0, this); } //sends player to hell void hell() { p[pC].inHell = true; addboardmessage("goes to hell!"); if (p[pC].money < 5) end(); } //removes player from game void end() { addboardmessage("is out of the game!"); while (!properties[pC].isEmpty()) { int squarenum = (Integer)properties[pC].remove(); sq[squarenum].disown(); } p[pC].playing = false; p[pC].y += 50; pL--; } //sorts lists LinkedList sort(LinkedList sorter) { sorter = (LinkedList)sorter.clone(); boolean[] sortarray = new boolean[36]; while(!sorter.isEmpty()) sortarray[(Integer)sorter.remove()] = true; for (int i = 0; i < 36; i++) if (sortarray[i]) sorter.add(i); return sorter; } void addboardmessage(String msg) { boardmsg.remove(); boardmsg.add("Player " + (pC+1) + " " + msg); } //changes turn void turn() { pC++; if (pC == pnum) pC = 0; System.out.println(pC + " turn"); if (!p[pC].playing) turn();//skip turn if player has lost else if (pL == 1) {//last player addboardmessage("wins!"); alert(okaymess, "You win!"); boardbtn[0].removeActionListener(this); } else if (p[pC].inHell) { alert(hellmess, "Pick a number to get out of hell..."); p[pC].money -= 5; } else if (p[pC].stuck) { p[pC].stuck = false; turn(); } } //makes sure player has enough money to pay boolean pay(int amt) { if (p[pC].money > amt) { p[pC].money -= amt; return true; } else return false; } //shows a message void alert(Message mess, String label) { dial = new Dialog(f, true); dial.setBounds(width/2,(height/2)-125, messagew, messageh+(mess.hadd*25)); mess.setLabel(label); dial.add(mess); dial.setVisible(true); dial.toFront(); } public void actionPerformed (ActionEvent e) { String butt = e.getActionCommand(); if (butt.equals("OK")) dial.dispose();//get rid of dialog else if (butt.equals("Roll")) { rollnum = rollcount = (int)(Math.random()*num)+1; going = true; } else if (trading1 || trading2) { if (butt.equals("Done") || butt.equals("Cancel")) { dial.dispose(); if (butt.equals("Done")) {//done selecting properties or money if (moneying) {//finish choosing amount of money String boardtrademess = boardmsg.removeLast().toString(); boardtrademess += "$" + trademoney + " "; boardmsg.add(boardtrademess); if (trading1) tradeoff.add("$"+trademoney); else tradereq.add("$"+trademoney); moneying = false; trademoney = 0; } if (trading1) {//go to second phase of trading trading2 = true; trading1 = false; trade(); } else {//finish trading trading2 = false; asktrade(); } } else {//cancel trading1 = false; trading2 = false; moneying = false; tradeplayer = -1; trademoney = 0; } } else if (butt.charAt(0) == '$') {//choosing amount of money, or choosing to choose it if (moneying) { int much = new Integer(butt.substring(1)).intValue(); trademoney += much; dial.dispose(); alert(moneymess, "How much? $"+trademoney); } else { dial.dispose(); moneying = true; alert(moneymess, "How much?"); } } else {//choosing properties String source = e.getSource().toString(); String propstring = source.substring(source.indexOf('[')+1, source.indexOf(',')); int prop = new Integer(propstring).intValue(); if (!trading2) tradeoff.add(prop); else tradereq.add(prop); String boardtrademess = boardmsg.removeLast().toString(); boardtrademess += butt + " "; boardmsg.add(boardtrademess); propmess.remove((Component)e.getSource()); } } else if (buying) {//buying properties dial.dispose(); buying = false; if (butt.equals("Yes")) {//player wants to buy property if (pay(curSq.price)) { curSq.owned = pC; curSq.c = p[pC].c; properties[pC].add(curSq.num); addboardmessage("buys "+curSq.name); } else alert(okaymess, "You can't afford that!"); } turn(); } else if (improving || unimproving) { if (butt.equals("Done")) { dial.dispose(); improving = unimproving = false; } else { String source = e.getSource().toString(); String propstring = source.substring(source.indexOf('[')+1, source.indexOf(','));//get number of property int prop = new Integer(propstring).intValue(); if (unimproving) { sq[prop].unimprove(); if (sq[prop].improvement == 0) propmess.remove((Component)e.getSource()); p[pC].money += sq[prop].price/4; } else if (pay(sq[prop].price/2)) { sq[prop].improve(); if (sq[prop].improvement == 5) propmess.remove((Component)e.getSource()); } else { dial.dispose(); alert(okaymess, "You can't afford that!"); improving = false; } } } else if (p[pC].inHell) { dial.dispose(); int outtahell = (int)(Math.random()*3)+1;//determine number required to get out of hell if (outtahell == new Integer(butt)) {//player picked correct number alert (okaymess, "You're outta hell!"); addboardmessage("escapes from hell!"); p[pC].inHell = false; } else { if (p[pC].money < 0) end();//player doesn't have enough money to continue else { alert (okaymess, "You're still in hell!"); turn(); } } } else { if (butt.equals("Unimprove")) { LinkedList impmess = checkimprove(true);//check to see if we can unimprove if (impmess.isEmpty()) alert (okaymess, "Improve something first!"); else { unimproving = true; propmessage(impmess, "Unimprove which properties?"); } } else if (butt.equals("Improve")) {//check to see if we can improve LinkedList impmess = checkimprove(false); if (impmess.isEmpty()) alert(okaymess, "You have nothing to improve yet."); else { improving = true; propmessage(impmess, "Improve which properties?"); } } else if (butt.equals("Trade")) { if (properties[pC].isEmpty()) alert(okaymess, "You have nothing to trade yet."); else trade(); } else if (butt.equals("Yes") || butt.equals("No")) {//player receiving trade has accepted/rejected dial.dispose(); String result = " rejects"; if (butt.equals("Yes")) { int offsize = tradeoff.size(), reqsize = tradereq.size(); alert (okaymess, "Player " + tradeplayer + " accepted your trade offer."); result = " accepts"; for (int i = 0; i < offsize; i++) dotrade(tradeoff.remove(), tradeplayer-1, pC); for (int i = 0; i < reqsize; i++) dotrade(tradereq.remove(), pC, tradeplayer-1); trading1 = false; trading2 = false; } else alert (okaymess, "Player " + tradeplayer + " rejected your trade offer."); boardmsg.remove(); boardmsg.add("Player "+tradeplayer+result+" the trade."); tradeplayer = -1; } else { try { tradeplayer = new Integer(butt).intValue();//player to trade with chosen } catch (NumberFormatException ex) { dial.dispose(); } } dial.dispose(); } repaint(); } void trade() { if (tradeplayer == -1) {//no one to trade with yet LinkedList tradebuttons = new LinkedList(); if (pnum > 2) {//ask whom to trade with int j = 0; for (int i = 0; i < pnum; i++) { if (i != pC) { j++; tradebuttons.add(new PB (""+(i+1), messagew/2-125+(j*50),messageh/2, this)); } } Message trademess = new Message(this, messagew, messageh, tradebuttons); alert(trademess, "Trade with which player?"); } else if (pC == 1) tradeplayer = 1; else tradeplayer = 2; if (properties[tradeplayer-1].isEmpty()) { alert(okaymess, "Player "+ tradeplayer + " has nothing to trade."); tradeplayer = -1; } else { trading1 = true;//start phase 1 of trading tradeoff = new LinkedList(); tradereq = new LinkedList(); } } if (trading1 || trading2) {//get request or offer int propindex = tradeplayer-1; String thing = "Request"; if (trading1) { propindex = pC; thing = "Offer"; addboardmessage("proposes a trade with player "+tradeplayer+"..."); } addboardmessage(thing+"s: "); repaint(); propmessage(properties[propindex], thing+" which?"); } } //asks trade offeree if they accept the trade void asktrade() { String ask = "Trade "; if (tradereq.isEmpty()) ask += "nothing "; else for (int i = 0; i < tradereq.size(); i++) { String req = tradereq.get(i).toString(); if (req.contains("$")) ask += req + " "; else ask += sq[new Integer(req).intValue()].name + " "; } ask += "for "; if (tradeoff.isEmpty()) ask+= "nothing"; else for (int i = 0; i < tradeoff.size(); i++) { String off = tradeoff.get(i).toString(); if (off.contains("$")) ask += off + " "; else ask += sq[new Integer(off).intValue()].name + " "; } ask += "?"; alert (yesnomess, ask); } //performs trade void dotrade(Object o, int pl1, int pl2) { String string = o.toString(); if (string.contains("$")) { int thisheremoney = new Integer(string.substring(1)); p[pl1].money += thisheremoney; p[pl2].money -= thisheremoney; } else { int button = new Integer(string).intValue(); properties[pl1].add(o); properties[pl2].remove(o); sq[button].c = p[pl1].c; sq[button].owned = pl1; } } //loads a frame if this isn't an applet public static void main(String args[]) { Shixl m = new Shixl(); m.init(); f.add(m); f.setBounds(115, 25, width+50, height+50); f.setVisible(true); } } class Player { public int Pos, x, y;//player's location public int square, money;//player's square and money Color c; public boolean playing/*still in the game*/, inHell, stuck/*on "sticky" square*/; public Player (int x, int y, Color c) { Pos = 0; this.x = x; this.y = y; this.c = c; money = 1000; playing = true; inHell = false; stuck = false; } } class Square { public String name, curname; public int owned, price, rent, x, y, num/*square's number*/, improvement/*number of improvements*/; public int[] neighbors; public Color c; public Square (String name, int num) { owned = -1; c = Color.white; improvement = 0; neighbors = new int[2]; neighbors[0] = 0; neighbors[1] = 0; this.name = curname = name; this.num = num; } public void setPrice(int price) { this.price = price*10; rent = price; } public void improve() { improvement++; rent += price; curname += "+";//add + to square's display name } public void unimprove() { improvement--; rent -= price; curname = curname.substring(0,curname.length()-1);//remove + } //takes property from losing player public void disown() { int totalimp = improvement; for (int i = 0; i < totalimp; i++) unimprove(); owned = -1; c = Color.white; } } //messages that pop up class Message extends Panel { public int hadd;//number of rows of buttons Label l; Message (ActionListener list, int dw, int dh, LinkedList buttons) { int cx = dw/2, cy = dh/2; l = new Label(); hadd = (buttons.size()-1)/3; setLayout(null); l.setBounds(0,dh/10,200,20); add(l); for (int i = 0; i < buttons.size(); i++) { PB b = (PB)buttons.get(i); b.setBounds(b.x,b.y,40,25); add(b); } l.setForeground(Color.white); setBackground(Color.black); setFont(new Font("Sans", Font.BOLD, 12)); } public void setLabel(String message) { int length = message.length(); for (int i = 30-length; i > 0; i--) message = " "+message; l.setText(message); } } //buttons on board class PB extends Button { public int x,y; public PB(String label, int ecks, int why, ActionListener l) { addActionListener(l); x = ecks; y = why; setLabel(label); setBackground(Color.black); setForeground(Color.white); setFont(new Font("SansSerif", Font.BOLD, 12)); } }