#define EOL '\n' #define ECHO int qx, qy, sx, sy, knum, klingon, klingonTotal, base, days, torpedo, casualty; byte gmap[64], smap[64], kx[6], ky[6]; int energy, kenergy[6], damage[7]; bool dock; void setup() { Serial.begin(9600); randomSeed(analogRead(0)); yesno(); } void loop() { int w1, w2, w3; Serial.print(F("DO YOU WANT A DIFFICULT GAME? (Y OR N) ")); if(yesno()) { w1 = 999; #ifdef ECHO Serial.println(F("Y")); #endif } else { w1 = 2999; #ifdef ECHO Serial.println(F("N")); #endif } Serial.println(F("")); Serial.print(F("STARDATE 3200: YOUR MISSION IS ")); do { klingon = 0; base = 0; days = 30; for(int i=0; i<64; i++) { w2 = (rnd(99) < 5); base += w2; w3 = rnd(w1); w3 = (w3 < 209) + (w3 < 99) + (w3 < 49) + (w3 < 24) + (w3 < 9) + (w3 < 2); klingon += w3; //1バイトに各オプジェクトの情報を詰め込む //7ビット目:0不可視-1可視 6〜4ビット:0〜6クリンゴン数 3ビット目:宇宙基地の有無 2〜0ビット:0〜7星の数 gmap[i] = 0 + (w3 << 4) + (w2 << 3) + rnd(7); } } while((base < 2) || (klingon < 4)); Serial.print(F("TO DESTROY ")); Serial.print(klingon); Serial.println(F(" KLINGONS IN 30 STARDATES.")); Serial.print(F("THERE ARE ")); Serial.print(base); Serial.println(F(" STARBASES.")); InitEnter(); casualty = 0; klingonTotal = klingon; qx=rnd(8); qy=rnd(8); sx=rnd(8); sy=rnd(8); InitObj(); while(1) //ゲームループ { DockBase(); KlingonAttack(); if(klingon > 0) { if(days < 0) { Serial.println(F("IT'S TOO LATE, THE FEDERATION HAS BEEN CONQUERED.")); break; } if(energy < 1) { Serial.println(F("ENTERPRISE DESTROYED")); if((klingonTotal - klingon) > 9) Serial.println(F("BUT YOU WERE A GOOD MAN")); break; } Command(); } else { Serial.println(F("")); Serial.println(F("MISSION ACCOMPLISHED.")); if(days < 3) Serial.println(F("BOY, YOU BARELY MADE IT.")); if(days > 5) Serial.println(F("GOOD WORK...")); if(days > 9) Serial.println(F("FANTASTIC!")); if(days > 13) Serial.println(F("UNBELIEVABLE!")); days = 30 - days; w1 = klingonTotal * 100 / days * 10; Serial.print(klingonTotal); Serial.print(F(" KLINGONS IN ")); Serial.print(days); Serial.print(F(" STARDATES. (")); Serial.print(w1); Serial.println(F(")")); w2 = 100 * (casualty == 0) - 5 * casualty; Serial.print(casualty); Serial.print(F(" CASUALTIES INCURRED. (")); Serial.print(w2); Serial.println(F(")")); Serial.print(F("YOUR SCORE:")); Serial.println(w1 + w2); break; } } Serial.print(F("ANOTHERE GAME? (Y OR N)")); if(!yesno()) { Serial.println(F("")); Serial.println(F("GOOD BYE.")); while(1) delay(1000); } Serial.println(F("")); } //1〜nの乱数 int rnd(int n) { return (random() % n) + 1; } //シリアル入力文字をバッファに収納 int keyin(char *keybuffer) { int chr, len = 0; while(1) { if(Serial.available()) { chr = Serial.read(); if(chr > 0) { if(chr == EOL) //改行コードで終了 { if(chr == 0xa) //CRLF対応 { if(keybuffer[len - 1] == 0xd) //CR --len; } break; } else if(chr == 8) //BSでバッファから1文字削除 { if(len > 0) --len; } else keybuffer[len++] = chr; } } } return len; } //Yes No入力 bool yesno() { char keybuff[15]; int keylen; char c; keylen = keyin(keybuff); for(int i=0; i= '0') && (keybuff[i] <= '9')) { num = num * 10 + (keybuff[i] - '0'); } else break; } return num; } //エンタープライズ初期化 void InitEnter() { energy = 4000; torpedo = 10; dock = true; for(int i=0; i<8; i++) damage[i] = 0; } //エンタープライズの位置表示 void EnterInfo() { Serial.print(F("ENTERPRISE IN Q-")); Serial.print(qx); Serial.print(F(",")); Serial.print(qy); Serial.print(F(" S-")); Serial.print(sx); Serial.print(F(",")); Serial.println(sy); } //各オブジェクトの初期化 void InitObj() { byte obj; int num, xy; for(int i=0; i<64; i++) smap[i] = 0; for(int i=0; i<7; i++) damage[i] = 0; for(int i=0; i<6; i++) { kenergy[i] = 0; kx[i] = 0; ky[i] = 0; } smap[8 * sx + sy - 9] = 4; //sx,syは1〜8 obj = gmap[8 * qx + qy - 9]; //qx,qyは1〜8 num = (obj & 0x70) >> 4; //クリンゴンの数 if(num) { for(int j=0; j> 4; //x ky[j] = xy & 0xf; //y } } knum = num; //セクター内のクリンゴンの数 EnterInfo(); num = (obj & 8) >> 3; //セクター内の宇宙基地の数 if(num > 0) SetObj(2); num = (obj & 7); //セクター内の星の数 if(num > 0) { for(int j=0; j1); i1); j 1090) { Serial.println(F("...OVERLOADED..")); damage[3] = 1; //Phaser故障 DamageReport(4); en = 9; } x = kx[no] - sx; y = ky[no] - sy; return(en * 30 / (30 + x * x + y * y) + 1); } //クリンゴンの攻撃 void KlingonAttack() { int en, etotal = 0; //攻撃エネルギー総量 if(knum == 0) return; Serial.println(F("KLINGON ATTACK")); if(dock) { Serial.println(F("STARBASE PROTECTS ENTERPRISE")); return; } for(int i=0; i<6; i++) //クリンゴンの攻撃エネルギー x 6隻 { if(kenergy[i] == 0) continue; en = Overload(((kenergy[i] + rnd(kenergy[i])) / 2), i); etotal += en; Serial.print(en); Serial.print(F(" UNITS HIT FROM KLINGON AT S-")); Serial.print(kx[i]); Serial.print(F(",")); Serial.println(ky[i]); } energy -= etotal; if(energy <= 0) { Serial.println(F("*** BANG ***")); return; } Serial.print(energy); Serial.println(F(" UNITS OF ENERGY LEFT.")); if(rnd(energy / 4) > etotal) return; else Casualties(etotal); } //クリンゴンの情報 void KlingonInfo(int no, int en) { Serial.print(F("KLINGON AT S-")); Serial.print(kx[no]); Serial.print(F(",")); Serial.print(ky[no]); kenergy[no] -= en; if(kenergy[no] > 0) { Serial.println(F(" **DAMAGED**")); } else { kenergy[no] = 0; int i = 8 * qx + qy - 9; //銀河系マップからクリンゴン1隻取り除く int j = (gmap[i] & 0x70) - 0x10; gmap[i] = (gmap[i] & 0x8f) + j; --klingon; //セクター内からクリンゴン1隻取り除く i = 8 * kx[no] + ky[no] - 9; smap[i] = 0; --knum; Serial.println(F(" ***DESTROYED***")); } } //コマンド選択 void Command() { char keybuff[15]; int com; bool repeat; do { Serial.print(F("CAPTAIN ? ")); keyin(keybuff); if((keybuff[0] >= '0') && (keybuff[0] <= '9')) com = keybuff[0] - '0'; else com = 0; #ifdef ECHO Serial.println(com); #endif switch(com) { case 1: Report(); repeat = true; break; case 2: SRSensor(); repeat = true; break; case 3: LRSensor(); repeat = true; break; case 4: GalaxyMap(); repeat = true; break; case 5: repeat = Phaser(); break; case 6: repeat = PhotonTorpedo(); break; case 7: repeat = Warp(); break; default: Serial.println(F("1=REPORT 2=SR. SENSOR 3=LR. SENSOR")); Serial.println(F("4=GALAXY MAP 5=PHASER 6=TORPEDO")); Serial.println(F("7=WARP ENGINE")); Serial.println(F("***PLEASE USE ONE OF THESE COMMANDS***")); repeat = true; break; } } while(repeat); //コマンド処理から抜けて次のターンに移るかどうか } //REPORTコマンド void Report() { Serial.println(F("STATUS REPORT:")); Serial.print(F("STARDATE ")); Serial.println(3230 - days); Serial.print(F("TIME LEFT ")); Serial.println(days); Serial.print(F("CONDITION ")); if(dock) Serial.println(F("DOCKED")); else if(knum > 0) Serial.println(F("RED")); else if(energy < 999) Serial.println(F("YELLOW")); else Serial.println(F("GREEN")); Serial.print(F("POSITION Q-")); Serial.print(qx); Serial.print(F(",")); Serial.print(qy); Serial.print(F(" S-")); Serial.print(sx); Serial.print(F(",")); Serial.println(sy); Serial.print(F("ENERGY ")); Serial.println(energy); Serial.print(F("TORPEDOES ")); Serial.println(torpedo); Serial.print(F("KLINGONS LEFT ")); Serial.println(klingon); Serial.print(F("STARBASES ")); Serial.println(base); for(int i=0; i<7; i++) { if(damage[i] > 0) DamageReport(i + 1); } } //ショートレンジセンサー void SRSensor() { EnterInfo(); if(DamageReport(1) > 0) return; gmap[8 * qx + qy - 9] |= 0x80; //銀河系マップで可視状態にする Serial.println(F("")); for(int i=1; i<9; i++) { Serial.print(i); for(int j=1; j<9; j++) { switch(smap[8 * i + j -9]) { case 0: Serial.print(F(" .")); break; case 1: Serial.print(F(" K")); break; case 2: Serial.print(F(" B")); break; case 3: Serial.print(F(" *")); break; case 4: Serial.print(F(" E")); } } Serial.println(F("")); } Serial.print(F(" ")); for(int i=1; i<9; i++) { Serial.print(F(" ")); Serial.print(i); } Serial.println(F("")); } //ロングレンジセンサー void LRSensor() { int num; EnterInfo(); if(DamageReport(3) > 0) return; Serial.println(F("")); for(int i=qx-1; i 0) * (i < 9) * (j > 0) * (j < 9)) { gmap[num] |= 0x80; //銀河系マップで可視状態にする num = gmap[num]; Serial.print((num & 0x70) >> 4); //クリンゴン Serial.print((num & 0x8) >> 3); //宇宙基地 Serial.print((num & 7)); //星 Serial.print(F(" ")); } } Serial.println(F("")); } } //銀河系マップ void GalaxyMap() { int obj; EnterInfo(); if(DamageReport(2) > 0) return; Serial.println(F(" OF GALAXY MAP")); for(int i=0; i<8; i++) { Serial.println(F("")); Serial.print(i + 1); Serial.print(F(":")); for(int j=0; j<8; j++) { obj = gmap[8 * i + j]; if((obj & 0x80) == 0x80) //可視状態か { Serial.print((obj & 0x70) >> 4); //クリンゴン Serial.print((obj & 0x8) >> 3); //宇宙基地 Serial.print((obj & 7)); //星 Serial.print(F(" ")); } else Serial.print(F("000 ")); } } Serial.println(F("")); Serial.print(F(" ")); for(int i=0; i<8; i++) Serial.print(F(" ...")); Serial.println(F("")); Serial.print(F(" ")); for(int i=1; i<9; i++) { Serial.print(F(" ")); Serial.print(i); Serial.print(F(" ")); } Serial.println(F("")); Serial.println(F("")); } //フェイザー bool Phaser() { int power, hit; if(DamageReport(4) > 0) return true; //コマンド入力に戻る Serial.print(F(" ENERGIZED. UNITS TO FIRE ? ")); power = getnum(); #ifdef ECHO Serial.println(power); #endif if(power < 1) return true; if(power > energy) { Serial.print(F("\"SPOCK: WE HAVE ONLY ")); Serial.print(energy); Serial.println(F(" UNITS.\"")); return true; } energy -= power; if(knum < 1) { Serial.println(F("PHASER FIRED AT EMPTY SPACE.")); return false; //ゲームループに進む } power /= knum; for(int i=0; i<6; i++) { if(kenergy[i] != 0) { hit = Overload(power, i); Serial.print(hit); Serial.print(F(" UNITS HIT ")); KlingonInfo(i, hit); } } return false; } //コース入力 int course(int *s, int *t) { int deg, ret; Serial.print(F("COURSE (0-360) ")); deg = getnum(); #ifdef ECHO Serial.println(deg); #endif if((deg > 360) + (deg < 0)) { return 0; } *s = (deg + 45) / 90; deg = deg - *s * 90; ret = (45 + deg * deg) / 110 + 45; switch(*s) { case 0: case 4: *s = -45; *t = deg; break; case 1: *s = deg; *t = 45; break; case 2: *s = 45; *t = -deg; break; case 3: *s = -deg; *t = -45; break; } return ret; } //光子魚雷 bool PhotonTorpedo() { int s, t, ss, ts, x, y, sl, ll; if(DamageReport(6) > 0) return true; //コマンド入力に戻る if(torpedo == 0) { Serial.println(F(" EMPTY")); return true; } Serial.println(F(" LOADED")); if(course(&s, &t) == 0) return true; Serial.print(F("TORPEDO TRACK ")); --torpedo; ss = 45 * sx + 22; ts = 45 * sy + 22; for(int i=1; i<9; i++) { ss += s; ts += t; x = ss / 45; y = ts / 45; if((x < 1) || (x > 8) || (y < 1) || (y > 8)) continue; sl = 8 * x + y - 9; ll = 8 * qx + qy - 9; Serial.print(x); Serial.print(F(",")); Serial.print(y); Serial.print(F(" ")); switch(smap[sl]) { case 1: //クリンゴン for(int j=0; j<6; j++) { if((kx[j] == x) && (ky[j] == y)) KlingonInfo(j, rnd(99) + 280); } return false; //ゲームループに進む case 2: //宇宙基地 --base; //宇宙基地を取り除く smap[sl] = 0; gmap[ll] &= 0xf7; Serial.println(F("STARBASE DESTROYED")); Serial.println(F("\"SPOCK: I OFTEN FIND HUMAN BEHAVIOUR FASCINATING.\"")); return false; case 3: //星 Serial.println(F("HIT A STAR")); if(rnd(9) < 3) { Serial.println(F("TORPEDO ABSORBED")); return false; } //case 4: //元のBASICではここは実行されないので勝手に追加 if(rnd(9) < 6) { //星を取り除く smap[sl] = 0; gmap[ll] -= 1; Serial.println(F("STAR DESTROYED")); return false; } //case 5: //元のBASICではここは実行されないので勝手に追加 Serial.println(F("IT NOVAS ***RADIATION ALARM***")); Casualties(300); return false; } } Serial.println(F("...MISSED")); return false; } //ワープ bool Warp() { int dist, dm, cr, s, t, ss, ts, x, y; dm = DamageReport(5); if(dm == 0) Serial.println(F("")); while(1) { Serial.print(F("SECTOR DISTANCE ")); dist = getnum(); #ifdef ECHO Serial.println(dist); #endif if(dist < 1) return true; //コマンド入力に戻る if((dm > 0) && (dist > 2)) //故障中でも2つ動ける { Serial.println(F("\"CHEKOV: WE CAN TRY 2 AT MOST, SIR.\"")); continue; } if(dist > 91) { dist = 91; Serial.println(F("\"SPOCK: ARE YOU SURE, CAPTAIN?\"")); } if(energy < (dist * dist / 2)) { Serial.println(F("\"SCOTTY: SIR, WE DO NOT HAVE THE ENERGY.\"")); return true; } cr = course(&s, &t); if(cr == 0) return true; --days; energy = energy - dist * dist / 2; //エンタープライズ移動 smap[8 * sx + sy - 9] = 0; for(int i=0; i<7; i++) //故障箇所の修理 if(damage[i] > 0) --damage[i]; ss = 45 * sx + 22; ts = 45 * sy + 22; dist *= 45; for(int i=1; i<9; i++) { dist -= cr; if(dist < -22) { smap[8 * sx + sy - 9] = 4; EnterInfo(); return false; } ss += s; ts += t; x = ss / 45; y = ts / 45; if((x < 1) || (x > 8) || (y < 1) || (y > 8)) { ss = qx * 72 + ss / 5 + dist / 5 * s / cr - 9; qx = ss / 72; ts = qy * 72 + ts / 5 + dist / 5 * t / cr - 9; qy = ts / 72; if(rnd(9) < 2) { Serial.println(F("***SPACE STORM***")); Casualties(100); } if((qx > 0) && (qx < 9) && (qy > 0) && (qy < 9)) { sx = (ss + 9 - 72 * qx) / 9; sy = (ts + 9 - 72 * qy) / 9; InitObj(); return false; } Serial.println(F("**YOU WANDERED OUTSIDE THE GALAXY**")); Serial.println(F("ON BOARD COMPUTER TAKES OVER, AND SAVED YOUR LIFE")); qx=rnd(8); qy=rnd(8); sx=rnd(8); sy=rnd(8); InitObj(); return false; } else { if(smap[8 * x + y - 9] == 0) { sx = x; sy = y; } else break; } } Serial.println(F("**EMERGENCY STOP**")); Serial.println(F("\"SPOCK: TO ERR IS HUMAN.\"")); smap[8 * sx + sy - 9] = 4; EnterInfo(); return false; } }