Xref: lugnet.com lugnet.robotics:57 Newsgroups: lugnet.robotics Path: lugnet.com!mattdm From: mattdm@mattdm.org (Matthew Miller) X-Real-Life-Name: Matthew Miller Subject: RoboTag version 0.8 -- explaination & NQC source X-Newsreader: slrn (0.9.4.3 UNIX) X-Coolwebsite: http://www.quotes-r-us.org/ Reply-To: mattdm@mattdm.org Organization: None Message-ID: X-Nntp-Posting-Host: jadzia.bu.edu Date: Tue, 6 Oct 1998 17:46:46 GMT Lines: 337 /* Matthew's RoboTag 0.8 for Lego Mindstorms. * * written in NQC version 0.5b1 -- http://www.enteract.com/~dbaum/lego/nqc/ * * Copyright (c) 1998 Matthew Miller. You're free to use and modify this as * you wish. You can also redistribute it, but please give me credit and * don't make any changes you haven't told me about. Thanks! * * mattdm@mattdm.org -- http://www.mattdm.org/mindstorms/ * * The basic game: * * Two robots run around a playing field. When one tags another, the * "victim" must sit still for a few seconds. * * The playing field: * * A big piece of paper, with a think black line for a border. There can * also be other lines on the paper for interest, although too many * doesn't work very well -- these aren't very smart robots. I find that a * simple partial line in the middle works well, like this: * * ------------------------------------------------------- * | | * | | * | | | * | | | * | | | * | | | * | | | * | | * | | * ------------------------------------------------------- * * The line is the border -- if the robot senses it, it must turn around. * * The only actual objects allowed on the field are the robots, so if one * hits something, it's safe to assume it was the other robot. * * The robots: * * Very simple. Treaded bulldozer-like robots, except that the 'blade' is * connected to a touch sensor, so that if it's bumped, the sensor is * triggered. (Shock absorbers, either large or small, are invaluable for * making this work. I'm really surprised Mindstorms doesn't come with any, * since it's such a useful construction. How do other people make their * bump sensors broad enough to be useful?) The light sensor is mounted * front-center and looks for the black line. * * Light sensor is on input 2, touch sensor on 1. Motors on A and C. No * particular reason -- that's just the Way It Is. * * The treads, by the way, have a 24t gear on attached directly to their * axles, and that meshes with an 8t gear attached to the motor shaft. This * gearing works well and is nice and compact. I wouldn't recommend it on * shag carpet or a dirty floor though, as the 24t gears are large enough * and close enough to the ground that they can gather junk. Works fine on * a paper play surface though. * * (I'll post pictures and a movie of these sometime soon.) * * The protocol: * * A lot of trust involved here. The RCXs are too dumb^H^H^H^H simple to do * otherwise. So, each robot believes what the other one says, and in fact * relies on the other to know if _it_ has been hit. * * If robot A hits robot B, A sends out a "Bang" signal. When robot B hears * this, it responds with "Ow" and goes into "punishment" mode -- sits * still beeping mournfully for a few seconds. If robot A hears this Ow, it * goes about its business. (Namely, meandering meaninglessly.) But if it * doesn't get the response after a few seconds, it starts spinning and * yelling "Ditto Ditto Ditto" until a response is heard. (Spinning 'cause * that has a good chance of making the IR ports line up, the lack of which * being the usual cause of lost messages.) If robot B hears a Ditto, it * responds with an Ow, and if it isn't in punishment mode already, starts * being punished. If already is being punished, it doesn't add to the * punishment (but still says Ow, just to get robot A to shut up). * * This may sound confusing, but it works beautifully. * * * Notes: * * Right now both robots run the same code. Maybe someday they'll each have * their own independent devious schemes.... * * For some reason, one light sensor gives readings about 10 lower than the * other. Which is sort of a pain. Originally, I just used a #define and * changed it before compiling/downloading, but that's annoying. So now the * robot reads 'normal' at startup, and assumes the dark line is going to * be about (at least) 7 darker than that. This is good for other reasons * too -- you could play on different colored paper without recompiling. * It also means you have to press "Run" while the robot is on the ground * on the playing field -- for some reason some of my friends' natural * instinct is to pick up the robot, press Run, and set it down. That won't * work. Future (fairly easy) adjustment -- make it so it also triggers on * 7 (or so) brighter than normal, for use with tape on carpets. * * Also, we used the Mindstorms/GUI to set the IR port into long-range * mode. This seemed like a good idea. We haven't tested it in * short-range. And I couldn't find any documentation on this anywhere -- * has anybody looked into this? Can any alternative tools set/read what * mode it's in? * * When I say "we", I mean Paul Stauffer, who owns the other RCX. I wish I * were that wealthy. * * When we first built the robots, my bump-sensor kinda sucked and was * breaking all the time and stuff. So I re-built it, and now it's really * good (I'm proud of myself; can you tell?). It's especially good at * making Paul whine, because in a head-on collision, my robot always * triggers first, and therefore wins. Hahahahahahaha. * */ int randnum; int bumpcount; int needackflag; int gotackflag; int blackvalue; task main { //set up sensors properly Sensor(IN_1,IN_SWITCH); Sensor(IN_2,IN_LIGHT); initlight(); //set trigger-value for border lines, since apparently //different sensors give different readings, and //because different surfaces obviously are different. //clear all flags bumpcount=0; needackflag=0; gotackflag=0; //start message-watching service start postoffice; //start black-line-watching service start checkline; //start bumper-watching service start checkbump; //full forward (um, the motors are connected backwards, so // Rev=Forward. sorry) Rev(OUT_A + OUT_C,OUT_FULL); while(true) //loop forever { if (bumpcount>0) // if we got hit { punishment(); // sit still for a while bumpcount -=1; // and then decrement hit counter } if (needackflag>1 && gotackflag==0) // if we need to hear ack of { // our bump signal and we haven't yet startspinning(); //start turning a random dir ClearTimer(0); //count how long we've spun while(gotackflag==0) //wait until we get the ack signal { SendMessage(34); //yell ditto PlaySound(0); //and beep nicely Sleep(10); // if we yell too fast, we can't listen. // so we have to pause for a while. 5 was // too little -- 10 works. haven't bothered // to find the exact value if (Timer(0)>100) { // it's been a long time gotackflag=1; //pretend we got an ack needackflag=0; //and stop caring. } } //ok done spinning now go. Rev(OUT_A + OUT_C,OUT_FULL); } } } // this task watches for incoming messages. task postoffice { ClearMessage(); //start by erasing whatever's in the air while(true) { if (Message()==33) //bang! we've been hit! { SendMessage(6); //send ack bumpcount +=1; //increase our damage. ow. ClearMessage(); //and of course clear the message now that // we've dealt with it. } if (Message()==34) //we've been hit but didn't respond. bad us. { SendMessage(6); //ack if (bumpcount==0) // but only do anything if we're { // supposed to be paying attention. if we're // down already, we ignore " signals. bumpcount +=1; } ClearMessage(); } if (Message()==6) //an ack { if (needackflag>0) //we only care if we're listening. { needackflag=0; //ok, so we don't care anymore gotackflag=1; //and we have what we're lookin' for } ClearMessage(); } } } task checkline { while(true) { if (IN_2 < blackvalue) //if the sensor detects dark { PlayNote(442,10); revturn(); //back up and turn Rev(OUT_A + OUT_C,OUT_FULL); //then go forward again } } } task checkbump { while(true) { if (IN_1 == 1) //if the bump sensor is pressed { SendMessage(33); //yell bang PlaySound(0); gotackflag=0; //clear old acks -- they're not relevant anymore needackflag=1; //we need an ack now revturn(); //back up and turn Rev(OUT_A + OUT_C,OUT_FULL); //the forward again if (needackflag==1) //if we haven't gotten an ack yet... { needackflag=2; //then advance to spinning around yelling } } } } sub revturn //back up and turn a random direction { Fwd(OUT_A + OUT_C, OUT_FULL); //back up Sleep(100); //for one second Off(OUT_A + OUT_C); // stop motors -- is this needed? probly not. randnum=Random(2); //kludge 'cause Random(x) doesn't work in an "if" if (randnum == 1) // 50/50 chance. { Rev(OUT_A,OUT_FULL); //turn uh, left or right. whichever this is. Fwd(OUT_C,OUT_FULL); } else { Rev(OUT_C,OUT_FULL); //turn uh, right or left. Fwd(OUT_A,OUT_FULL); } Sleep(50); //keep turning for .5+Random(3) seconds Sleep(Random(300)); Off(OUT_A + OUT_C); //stop motors } sub startspinning // start spinning a random direction. { // if we could nest subs, we would. (same code above) Off(OUT_A + OUT_C); randnum=Random(2); if (randnum == 1) { Rev(OUT_A,OUT_FULL); Fwd(OUT_C,OUT_FULL); } else { Rev(OUT_C,OUT_FULL); Fwd(OUT_A,OUT_FULL); } } sub punishment //bad robot. must sit still for 8 seconds. { stop checkline; // ignore everything else. we're DEAD. for now at least stop checkbump; // note that we still process messages. Off(OUT_A + OUT_C); repeat(4) { PlaySound(4); //cry Sleep(200); } Rev(OUT_A + OUT_C,OUT_FULL); // ok go forward again. start checkline; //and pay attention to stuff start checkbump; } sub initlight //reads the value of 'normal', and assumes that the { // black-line border is about 7 darker. no real reason to // put this in a sub, except for neatness. blackvalue=IN_2; blackvalue-=7; //may have to adjust this. //also -- what about white lines? } -- Matthew Miller ---> mattdm@mattdm.org Quotes 'R' Us ---> http://quotes-r-us.org/