# run this program using Plumb and Animator, like this: # plumb "proc lander = python lander.py ; proc anim = animator ; pipe lander anim ; pipe anim lander" import base64 import zlib import random import sys from time import time, sleep from math import floor, ceil, pi, cos, sin RESOLUTION=64 MINPEAKS=3 MAXPEAKS=6 VARIATION=3 MAXHEIGHT=1.0 MINSITE=1 MAXSITE=3 MINSITEW=8 MAXSITEW=14 VIEWPORT=50.0 TERRAINHEIGHT=25.0 LANDERSIZE=5 MAXVARI=0.2 MAXLANDVX=1.0 MAXLANDVY=2.0 STARS=100 MINDELAY=0.01666 GRAVITY=1.6 FUEL=500.0 LATERALSTRENGTH=2.0 VERTICALSTRENGTH=4.0 CONSUMPTION=10.0 ROCKETPARTICLEV=8.0 ROCKETPARTICLEVAR=2.0 PARTICLEGRADIENT=[(0, 1, 1, 1), (0.3, 1, 1, 0.125), (0.7, 1, 0, 0.125), (1.0, 0, 0, 0.125)] PARTICLEVERTS=7 PARTICLEMINSIZE=0.1 PARTICLEMAXSIZE=0.3 LATPARTICLEPULSE=1 VERTPARTICLEPULSE=2 EXPLODEPARTICLES=50 EXPLODEV=16.0 EXPLODEPARTICLEVAR=10.0 EXPLODEPARTICLEMULT=4.0 BLINKRATE=15 SHOWFPS=False MOONVERTS=48 MOONR=24 MOONVAR=3 MOONCRATERS=18 MOONCRATERMINSIZE=0.5 MOONCRATERMAXSIZE=2.5 MOONCRATERVERT=16 NUMGAMES=0 NUMVICTORIES=0 DIFGRAVITY=None DIFFUEL=None DIFTERRAIN=None lander='eJxtW8uuLLcN3J+vaCBrDySKEqXP8b2wkQAGHMTZ5O/DqmLPzDEmC+fodrdEUnwWOX/981+///f6pT3crvY49vXXz1//+C3/bK0H/h3///Xzzz/+/M/1j9/5v+vXf//5x/++znnEtXZ+eb767I9xxbJHnC/r+2HzirMe63yNfh4nH43HzMXuD7vW2o+Ri/BHzGvG4XvGleOZf42WG+bKxuPk9rFye7eJPfo6j40XB082PJpjPY5/ncDBqy+Q8U5f0f+z/+w/fiv6k5YrWn6Rp8UEjW3x6OgPPPHHzMVaj52L/thYjGudZC6pnUlELgZOGjNA7Noifc7HwqqDpDEdLyarYH+mSK41DEzhiSf7x7RHS3JnDB7k+Xo+yp3w4ph8cRxSZPn1NSEtCK1ds23+2dflZz5avtKTg5TQHly1mRR4GOi24/m1T3ss/7I980xPcno+Wbgnt/bYuUhW9jVOB0OWF5kCxwo8pKxWktZIaBLfk6H+aOddoJ/k3XfggFSH3LNv8uRuD8dqcbUcIutQg5Re5J+LFKbMk9yeMrdr9gl16SnKDqFQlh3kXNMneOwpPU8Kg9oyArcxo0Hl+sDdzu0gsqc+5XupJ3jP8ASqg+1Sg5PjlVq1scK5K08P7GC8T+NHDpZW8tKgl4P3nko4wcR5tDSC3nAHYHDlYj96Lk7Di2FUt55i9SuGSNoHuuddMsp/zxO6DAAmlWaG1SLvMYv3tBGtQIUv7DA2F0OP+qBcDI+gwJBsh3FQ23rqfdKd958vpX6khkLx8IDW2o2k5V7J0LTgzimAJSPEZUTgmQ9xlAaU6pJE4moDhw6nWn1TgY86stKSrmFdAt2gfiT1wV2vkZSC4LSStDdYAk54/+bjpkVrugJIHw4GK1oNFAymciTmZCfFPNvzQsFxp+X0BUOeeYVTm8B1WZE2+Znp5jdEOO2Q40iNzbulxm54Kh/SnIHdpmRkVDcYPwjML/O9ZG7pcmnKJst45+SzkUGZDvXMz7UbfUbQI+1U7lykvif/u4SX9p4y3qPRwgyavpNeXGNajudCNpCmha9Sr7qoalrhxZW2lQtaPm6Dj8qUBzfsdNWRZpwH0zvDInaSpP3olXZedNqotTy18zYtbd3IBHwS/GouNuRn6XlzkdblcIkd8t/dFEg6pFlbD6NVbpMvtEUSgj48mXbxnttDRblakN1Ia3JxCN+6ntzDpae2b70Jf0xXCRaxOOJeYcu7c9XoieBnM8gcChceOC9q8xKcBhOIa/D98KKTtKfJRVrvMz5mTIghJlNWSUM43QaiJVYZUUzO+zyDcd794N4LPp6CiuDlGHwLVlRpG7iq2JSo5Zmp31ghTjRyhRfhbw5dd4Si8cYVRbpXk5tajPVNcTrJmIrgSx+59PhNSz+GZqhZeqpO5mAduaB7y4AHwtahpx894K1S6AiTnVaIWD1fAkqP0bWC/1UY7yFpLe445TCXVOLt4KKs8X+3fTlUvVFjHOFjyzsgHkDnDj5E4gMlmDTnZBarFC9sxco85EVH6dW5fQJ0rnzllFJnLEAAmbXLofxSmlO7LPnbTYvDcREy70OT27J8RfWeRnPoBjoulZ4FRrdx4S4GlBjMchj9zhmG3uzKP8D2lOUNbg6rSP3c4pqJiWwkyWAGIwqVAyZHR8a0lR8ZuXTYGVw5FguG1cgvrg8XIg6xOOVG8iVHwKYfSLkj/kBPYYQwMSZ1m7fgQUkH7gk2ZjC4VGsQnpeAr5IvJKWgH2Y2uepwTOHMLkczmONkimAZqWGO0GtZ2dL2SJgWQ3rwdmBl2H5TpwzpA1aUJcysawU5I//ki0su0WVzVqlAKNwr2ODgrUDk0mWmS4r3qZnQizddLV3+EZmQ3LrswzORoaxdzg9pKhTbeeErKHxP7pndynHJaaxNRcTqyObWkVNDusGE0iGoSfeVpCFTmEp6cAIEBSkzfUCiSufQ+N3WnnIVfpiNJ0f47KR8/PB2D33jpMAdzgFSoPn5Zr4amYAMHAX3EcFqAdXFlF8zUcjLYBQG16DiMPP0jApTuhTw5HCpO90y9kdCwqgEahszARoHVrKwoKUcRlNTppwCg3GnXbD02dgCZnFYY/T5NI/Zn9HEp745dFQpnvO0r8FSZ5tMJS1D8Z0LOY7md5AjxwpkgwlEKO/ACupnTBbxJkodWcgohdi0y4HiBNph2n/iUZNpj6ropiqVdNWL9jckz7UqoEKCeAQSJ0ID9oMs0lpUqGHzBaVFRm1yB4uqRpEtun0WGjAUuH28dOhpVjpdqAXy7UkPDr2ATTPNxd281P0uZ/uPeFoDi52UGuOAVyw5UqFB5wtXAj1ZTJmgUdCT3eUHOvVkV2YeUsSQtOncsIImU1K+6cGh11DyDb2WLQTTM5QAyCCiyRvJ2Sk9jUGHe9uXyWYnXCwqDH+ZZWcodAYqFLRDWj4orSlDX6xb+dGgsJhf+GB9G0zwPJUNQTcYtFyXHEzjUqiUt3ydV3HEZMNt8yRWD7gJhmNTYsQICfqwSksqRwHRN70ptsaRnIxFC3KtlwAAJiByd+pyuDxKk+YgjkcaG6OKIfhXBjghpXUX4Ni7HVX01JWjgDWl41uWJ78Glqm81Hg5lKHiC8YAIlh8oZKi9TYqJq5QmUWbLxsK7phKT8vWTW7lIC5tQwAQJfAvTSyr6oUjgjhS6RCmm9JS8syUWFX64IKoy8ATOCz/ruefUhxvqqw7SyJYwkCdTSm78bqny/cN0jaTX5eWnCynO+Otgvwqt+gs2BYKITxDIrxS90IvQtJKXaF2eHGVxVKc0aVBDO5rV9xH2oBaFkbfGG+bQjY8XS7kuLb8XSvAKRj0u0CeTZsI+RljEBsEecpAcNdp60CUju4wfZjRnUD4sPEkTnnQTIaU4eGKDlGdKfXoquzG854B6wzpAKq+IdU2yVkVJZMsFKKsKAfzZUS8SmynABIc3EAS3ARSeOFfPhuT/U2Nc2OkNuJQLsVh0p6PumtlXHlBFXQywGKQkwavmKchizcSsoX5Ib8iHIP81MSMcpT0P5NwDFJeJ+aS4kJaM2imgGMIugSq4E2orRuDOPCYQ3wCriT/iyo1D4H6MHZigUhCi+vlwWYBR/SWvanIoNLBJXIVd/7B6nMjAayUWegT0g+W7/32zEQ2qPBMOoQBpFuZOjot8/AJi+VKG5W89aNESPsr0y6fu5ccBmspZFgEk1KE+V84WeV1i5o26ZjXJCTTcM5CtbWcfn0pExt0osEgaswbBD2tTpQgXRiwicPcCjBH2quUoBGncAauzpwG2sciheBEY+LWlR77YbULeMmJLOI9RSBfelHFpS8VLEpFiTQWzHLBDbB6cT4q6S+74PJC6nUu1Oh8i5uPNAHiKCDJDsN3qahVbpxvWcoCVUxDrmdSDTPAgDZV7UBkF41AZXDnExbIvCv8G9L6Qy9jggtHinBdVkhuJ0GERg8tNB9JKqh2VpLqr2Jn6O6REXVAVc7gIzByyA+OgfxwKAXAAiDrVE3izDHGUuqD6ugauymt6jjriECX6baCMAbhNdN7wuTMn9ENMhcoAsQGlyMaYwljUjYqBQUiZooW8EOmVIsW3xT5G1HM2ZXcfAsaH2v6Pqg3VpX6hcx+C56ehKdRLBHWcTlIlFj5zaRHBFbtfIJKCbkXVI0e32YQ4GauZo48E7wAPxroAfgm/mbKhJGiYT9kzs8FQCVSB+0C3J3bFZgX2GEVaI2y0aeQva0OhQsfU6CBTlOL6CcHoxh4EtKNkw7LBfCxVFHSZITptCVbwp+H2wXJexPdR8zvwLVDFVjjE0uEKiAisGQdcsSoL2VroKpBuFCLhjqUYAkSJRPqlouQqIVhDeXQ1ilDxNglsQ2qIuyoQTmGuhkAF1JjjwTfev65VNXySluT4RINbnK0h1Um8nfegStAcTcA5vlV73LkCzfSCt9g5fgMa1RJWoMXGDUK5w3ZoZjCoyC7RLBQVw1VktiQ4mPCgNvHV84gDzJeov0m9U/9sS5XgiyTQqB7H7p4SI7PBGZ25nHIPJYuA+i26iusDCuqo6G0Tp6YGuIK2ZeRu2sUIGTedAGTUiepB+rYSrSK/epvdTZwXChZwfNN0C2i8oXU6igo8ij1VOLUVQ8BTmBst7sTsEj70Htiq0uyUyzjBjZ9X0pj+t8k9RF2O/p46UgkINx3SMUXuw1bSpgXGyYgmY2BOLfyJ80q9a3x7k69xrg1lPGBFrhXpTiH2Q/Y9mcKMc645eMyhSH7h4CUlVWAYk2ibAs3s+pNOCjUuWwFvfP1ybhvvQkGxVxdxA5AdntIL7bsqj/PSxZCCrS1UiRp8g52L8g4hbX8b+d8pGQBvkdkgtACwRWxCME1iBbDMAg/O9+TZ91yR7OQREahUZH3SDmawm6QLnpP3RuFXk+2EOujNuR+bXXkzXQM4Bf2NUEBkJl+buLgPfCIdBPBOcURsmCY1uIN3o/QPXx+NOXca0Pf8jk6y19EeJEkAr29EQ5o68mSnhSz9VHJwV/S0TG35ETCLVVSd8tbhN938X5Ln9ySxdK+LlENuaV9R1wjLZQ8ivmhYhYYsFM7TAu4xa12w2GpN1ToG30IoC98tfkI7nkKJA15HuRqS/6FSmpT8YKhAIjpkhyxvRNXhtZPCojULwUn4RxMZySjLRcLwc5ysUMpH9NReHmsgmUdqzP/m0Q+uR8TbIhzpnRf0aM91Qv+p0mcDDP0mtBDpwPCi/Vos0d6f1ZC045H0q3DTqhlY5SMHmnQ4P5K2MO942Sor8PqUdFRnxWN2rHI/8bZR7MnfgovgW+JW8LPlNzyqkMGE9IJoB1OAU+5vKbRgvXsCFt0UcNCp5BzvGn+/bBPmLUJOYLqgYLRLpT42MdLGNLJ5RIvQRr5EKQWQxnYvJNkO+cuhMcrOROOen8lyzi4IVerI90Qx1HqNeavVq6LdQ4w0WcyO5aywMbFVruA3sUVxpeSXDLBys2rdTeY1y5FfuMdAsCGfDsHYuqRkglU7Mxl4dUBUg4Fe+X5+9xzB+77zliRsIYSInpMUyEXMHyO3jifdLbsQ0MN/ZmIoaSkAY9TSTMTPrWW4PqO0hnGKVXo36/vEwwLpjtfp0+WU6DMO49udxxRofPKr7vSrHqiHs1RRVpXqFS+K3oqRHQJMIJ7S7SQMMumqQvZkhJ2IB5F+Cp3MKWerptrBHPwbCizrWrraMKDq3nnXxps2pIUk7G488BNpW2aovL5zFg45gS5gcR3GX20klckWi+fee4QI9OYijK3e6l9D59ArIPWfdSTH0+XvFVWLS5YWb+f9Yka9GzB5FBdaqFKaapjABwPsF81NTI4u8BNtmIBa6M0GusuWI/6FhhiCnWEgyWjIF3jYAJrRk0CuPAwVlGydV+C0Zja48JQiPUp01ATolPndV9DndGbevYbvQaevjH2sei1GnN6drLH60yg2HQC49mKAXFLbRoI4gg0EAQ3RRsaMxQLCdiskGux6FQ0RWCTm7NOwwJScfVljCL3u4ErxgU22xB1kmR/o5wSUWf6O1Mf9Y8FG26QblCxYBB+NnqNoT428g3WV7JqpQ5D0x6AmC42lLBgKlLFNWZhsF/hAgQU2YY6RAnW89E7FR/J3MRL+ZGICaxC+SUxotMVJjhQZ+XeNp0CokUlPLCu1pWIihz1OWwLxmlKqFAp8VF1jDPVUiUMPONcyHubwA1AXSrgUbdePPEOG7ZVCmxhU2xv57GXFczxjamPfV8OwQBABcZu6u2oAsEjrvxG+29I8rCFE1yg+STVx4td7dHFPZoWgB8Fk9SjyUQAX6HtMUPbc8O5RceoBVtHzlG6qTaDCQCtrtIb8R+9DXoT7FcQ6NJA11BXh1EOo4tDTRHAzWKcMAFwaXVXDwtygKZLK4ihyXw4ordq/lLp81IQgHtGL6M8DO4pF/JmR+iuRljZ1XpuqIIchzW9CLJqYBRhE0SKkIhruoZMlz4aahBoeNSokMQuuCCQ+CaNT+LqlHs1dDrGP9jPAWTSNISqAvcQJV2COI+QZyUFJ2qmFcFNoCDkwR0WUeyhvdmxWU0DllO9YbUBsNnc9ywiNxA9TZIXannYNLYaTWxsD7BZDUotBahZMsDWSbUaET3/VDsa057oUEyh/Cb5uUaa3oXw0Y9jCgf3sO7OHj5eW130TVA97gEztnj0YgudwkY3DDRPGcyrDPplFC0S8tD4oCk955zlUuqsvopmiUzTihj+pQtZd8uIu1Ny08pjwUwwrUiYUY96KPKZHnBmWGex0kK4PKJDPX/aTcHWqVFz3PjzSxQfK4bRRfMilXY4DslhL6v+oSkJ48wtJiU5csZKZI52J7h3P0kr55A3sjD2f9A2K3iyZqNN8Nh5ttSwGq+vTAZDuBqZlshgEnYk1cqmOT1cxLdBWyr8VE8KfPu2oJXJJxhnzXArS5QfSXgop+sUPmngR5WCpmckfq5HZvyEMiLrNqre4dauBO1dzJ+xI3o/zierMEA/eodWjb3MeSOxq5rT54bjYJzb73G/8mNYoc90NAkIHG61ds+T4f6ioGFekoYEkBz764nR90m9CWKxc6rFfgaO78R/dF0EmRGCgGyZBj8Kw0fbhcN/puHwptGAqb7S4Kjk0egcOu+FBAPe4CxjF3jJ6UjN2G0Ox2y/0VDjoNAWfMyxoa1xZ2oxhm+mUGdO1WnymGOemJxrmuHWwGBN0GokNzRcW+OD7J5OTv2oJ6aR3JozNvb77Tmx3vURBhDn4zmMCKiDh2KQRfOovQt15RwBYM7OwVq66cVJAWUl4osZafHIcVVJcwsThZi3Ji7ZNtUEWWhEjQ4N5h9TB4HNUPdSMx9qxyLvC42ZawKJHUm24asLqxEf/TYhNJpRlfTbYmgaf2sSh23WTVHNe6goGZyaMNgsXlZNb/BaF8EkzCs19lMRlTTWE1MZDRU4jgYWKBciPBiXT8Iw4cV/B9OaoMtEJzhx3WlOw78r7acCmD91QXs4dw9lY/qxQWyFTHY6d5HHPG0rW1q8HY4/kO482MQRHxBVxRdTk/1Dmx2F5iNTc42ocqZPg7tAEjk8U13+pqnj+j1G19Rdv2cDMBXVtOiamKrJ99AwldfYAFSMus5h0upFMHHBtLRrGJaGRCCxj13jLEs/wZBRwT3Z0rQbyx403he3CDGJkxgQg/oQh9igftQjHH9Kr5GdYia52qqcHJAmqXiKQtlajWjSM2rmswp0qX8vrdSvoVj+UBJegKg8kQoZ5PlhTY01jUAJANUol+BPTmXrVzU1mtoEuJ2aWeGguKZUjn6gw5loDVNp20bPh7SUX4Brzi5obBNKyncaDcL1Gxb4gq0ftNA7hyl6EDySX4Ozzb3eNfb/D41VWA==' print "macro lander" print "scale", LANDERSIZE, LANDERSIZE print "shift 0 0.5" print zlib.decompress(base64.b64decode(lander)) print "endmacro" peaks = random.sample(xrange(RESOLUTION), random.randint(MINPEAKS, MAXPEAKS)) peaks.sort() def genter(peaks): ter=[0]*RESOLUTION var=float(VARIATION)/RESOLUTION hvar=var/2.0 for i in peaks: ter[i] = random.random() last=ter[peaks[0]] for i in xrange(peaks[0]-1, -1, -1): last += random.random()*var-hvar ter[i]=last last=ter[peaks[-1]] for i in xrange(peaks[-1]+1, RESOLUTION): last += random.random()*var-hvar ter[i]=last def filbtwn(peak1, peak2): h1=ter[peaks[peak1]] h2=ter[peaks[peak2]] hd=(h2-h1)/(peaks[peak2]-peaks[peak1]) last=h1 for i in xrange(peaks[peak1]+1, peaks[peak2]): last += random.random()*var-hvar+hd ter[i] = last for i in xrange(0,len(peaks)-1): filbtwn(i, i+1) nsites = random.randint(MINSITE, MAXSITE) for s in xrange(nsites): p = random.randint(MINSITEW/2, RESOLUTION-MINSITEW/2-1) sw = random.randint(MINSITEW, MAXSITEW) fromp = max(0, p-sw/2) top = min(fromp+sw, RESOLUTION-1) ters = ter[fromp:top+1] h = sum(ters) / len(ters) for i in xrange(fromp, top+1): ter[i] = h mmin = min(ter) mmax = max(ter) msiz = mmax - mmin smin = 0.03 smax = min(mmax, MAXHEIGHT) ssiz = smax - smin return [((t - mmin) / msiz * ssiz + smin) * TERRAINHEIGHT for t in ter] def color(r, g, b): r=max(0.0, min(r, 1.0)) g=max(0.0, min(g, 1.0)) b=max(0.0, min(b, 1.0)) print 'color #%02x%02x%02x' % (r*255.0, g*255.0, b*255.0) def gradient(g, p): for i in xrange(len(g)-1): (ap,ar,ag,ab)=g[i] (bp,br,bg,bb)=g[i+1] if p >= ap and p <= bp: p = (p - ap) / (bp - ap) color((br-ar)*p+ar, (bg-ag)*p+ag, (bb-ab)*p+ab) return (bp,br,bg,bb)=g[-1] color(br, bg, bb) def newstars(): print "macro stars" for i in xrange(STARS): b = random.random()*0.5 + 0.4 color(b, b, b) print "dot", random.random()*VIEWPORT, random.random()*VIEWPORT print "endmacro" def newterrain(): global terrain terrain=genter(peaks) print "macro terrain" print "color gray" print "poly" step=VIEWPORT/(RESOLUTION-1) x=0.0 for i in xrange(RESOLUTION): print x, terrain[i] x += step print VIEWPORT, "0" print "0 0" print "0", terrain[0] print "endmacro" def genmoon(): print "macro moon" print "color #a0a0a0" print "poly" step = pi*2/MOONVERTS a = 0 for i in xrange(MOONVERTS): r = MOONR + (random.random() - 0.5) * MOONVAR x = cos(a) * r y = sin(a) * r print "%.4f %.4f" % (x, y) a += step print "color #808080" for i in xrange(MOONCRATERS): a = random.random() * 2 * pi size = random.random() * (MOONCRATERMAXSIZE - MOONCRATERMINSIZE) + MOONCRATERMINSIZE r = random.random() * (MOONR - size * 2.5) + size x = cos(a) * r y = sin(a) * r print "fillcircle %.4f %.4f %.4f %s" % (x, y, size, MOONCRATERVERT) print "endmacro" def selterrain(mode): global DIFTERRAIN, MINPEAKS, MAXPEAKS, VARIATION, TERRAINHEIGHT, MINSITE, MAXSITE, MINSITEW, MAXSITEW MINSITE=1 MAXSITE=3 MINSITEW=8 MAXSITEW=14 MINPEAKS=3 MAXPEAKS=6 TERRAINHEIGHT=25.0 if mode == 'f': DIFTERRAIN=" Flat " TERRAINHEIGHT=16.0 VARIATION=1 MINPEAKS=1 MAXPEAKS=3 elif mode == 'b': DIFTERRAIN=" Bumpy " TERRAINHEIGHT=28.0 VARIATION=3 MAXSITE=2 MAXSITEW=12 elif mode == 't': DIFTERRAIN="Terrible" TERRAINHEIGHT=37.0 VARIATION=5.5 MINPEAKS=8 MAXPEAKS=10 MAXSITE=1 MINSITEW=7 MAXSITEW=10 def selfuel(mode): global DIFFUEL, FUEL if mode == 'l': DIFFUEL='Little ' FUEL=250 elif mode == 'a': DIFFUEL='Average' FUEL=400 elif mode == 'm': DIFFUEL=' Much ' FUEL=650 def selgravity(mode): global DIFGRAVITY, GRAVITY, LATERALSTRENGTH, VERTICALSTRENGTH, CONSUMPTION CONSUMPTION=10.0 LATERALSTRENGTH=2.0 VERTICALSTRENGTH=4.0 if mode == 's': DIFGRAVITY='Slight' GRAVITY=1.5 elif mode == 'n': DIFGRAVITY='Normal' GRAVITY=2.5 elif mode == 'h': VERTICALSTRENGTH=5.5 DIFGRAVITY='Heavy ' GRAVITY=4.0 selterrain('b') selfuel('a') selgravity('n') print "bg #000020" print "color #202020" print "line -1 -1 -1 1" print "line 1 -1 1 1" print "color gray" print "shift -1 -1" viewportscale = 2.0 / VIEWPORT print "scale", viewportscale, viewportscale print "events on" def drawlander(p): (x, y, vx, vy) = p print "push" print "shift %.4f %.4f" % (x, y) vx /= 4.0 vx=min(1, max(-1, vx)) print "rotate %.4f" % (-vx*15,) print "invoke lander" print "pop" def updatepos(dt, p, fx, fy): (x, y, vx, vy) = p vx += (fx * dt) vy += ((fy-GRAVITY) * dt) x += (vx * dt) y += (vy * dt) if y<0: y = 0 return (x, y, vx, vy) def tersample(x): x = min(VIEWPORT, max(0, x)) / VIEWPORT * (RESOLUTION - 1) f = max(0, int(floor(x))) c = min(RESOLUTION-1, int(ceil(x))) if f == c: return terrain[f] m = x-floor(x) return terrain[f]*(1.0-m) + terrain[c]*m def mainmenu(): s = { 'r': 0, 'messagecnt': 0, 'messagestate': 0 } centermsg = " L A N D E R\\n\\n SELECT TERRAIN\\nF:LAT B:UMPY T:ERRIBLE\\n SELECT FUEL\\nM:UCH A:VERAGE L:ITTLE\\n SELECT GRAVITY\\nS:LIGHT N:ORMAL H:EAVY" fr1 = "* * * * * * * * * * * * *\\n *\\n*\\n *\\n*\\n *\\n*\\n *\\n*\\n *\\n*\\n * * * * * * * * * * * * *" fr2 = " * * * * * * * * * * * * *\\n*\\n *\\n*\\n *\\n*\\n *\\n*\\n *\\n*\\n *\\n* * * * * * * * * * * * *" def frame(): print "frame" print "color #000080" print "line 0 0 0", VIEWPORT print "line", VIEWPORT, "0", VIEWPORT, VIEWPORT print "invoke stars" print "push" print "shift", VIEWPORT/2, VIEWPORT/2 print "rotate %.4f" % (s['r'],) print "invoke moon" print "pop" print "color white" if NUMGAMES > 0: print "text center top %f %f \"WON %s OF TOTAL %s\"" % (VIEWPORT/2, 0, NUMVICTORIES, NUMGAMES) print "text center bottom", VIEWPORT/2, VIEWPORT, "\" PRESS SPACE TO PLAY\\n%s * %s * %s\"" % (DIFTERRAIN, DIFFUEL, DIFGRAVITY) print "color white" print "text", VIEWPORT/2, VIEWPORT/2, "\"%s\"" % (centermsg,) if s['messagestate'] == 0: print "color green" else: print "color yellow" print "text", VIEWPORT/2, VIEWPORT/2, "\"%s\"" % (fr1,) if s['messagestate'] == 0: print "color yellow" else: print "color green" print "text", VIEWPORT/2, VIEWPORT/2, "\"%s\"" % (fr2,) print "flushdelay 0.0166666" def update(dt): s['r'] += dt * 2.0 while True: l = sys.stdin.readline().strip().split() if l[0] == 'KEYDOWN': if l[3] == 'space': return True elif l[3] == 'escape': return False elif l[3] in ['f', 'b', 't']: selterrain(l[3]) elif l[3] in ['l', 'a', 'm']: selfuel(l[3]) elif l[3] in ['s', 'n', 'h']: selgravity(l[3]) elif l[0] == 'FLUSHDELAY': s['messagecnt'] += 1 if s['messagecnt'] >= BLINKRATE: s['messagecnt'] = 0 s['messagestate'] = 1 - s['messagestate'] update(float(l[1])) frame() sys.stdout.flush() def game(): keystate = { 'u':False, 'l':False, 'r':False } keymap = { 'up': 'u', 'left': 'l', 'right': 'r', 'w': 'u', 'a': 'l', 'd': 'r', '[8]': 'u', '[4]': 'l', '[6]': 'r' } s = { 'lander': (VIEWPORT/2, VIEWPORT-LANDERSIZE, 0, 0), 'fuel': FUEL, 'parts': [], 'drawlander': True, 'alive': True, 'message': None, 'messagecolor1': None, 'messagecolor2': None, 'messagestate': 0, 'messagecnt': 0, 'win': False, 'fps': None } def shoot(x, y, vx, vy, varx, vary, sizemult): vx = vx + (random.random() - 0.5) * varx vy = vy + (random.random() - 0.5) * vary size = sizemult * (random.random() * (PARTICLEMAXSIZE - PARTICLEMINSIZE) + PARTICLEMINSIZE) return ((x, y, vx, vy), 0.0, size) def rocketpulse(vx, vy, n): (lx, ly, lvx, lvy) = s['lander'] vx *= ROCKETPARTICLEV vy *= ROCKETPARTICLEV s['parts'] += [shoot(lx, ly+LANDERSIZE/4, lvx+vx, lvy+vy, ROCKETPARTICLEVAR, ROCKETPARTICLEVAR, 1.0) for i in xrange(n)] def kill(reason): s['alive'] = False s['drawlander'] = False (lx, ly, lvx, lvy) = s['lander'] ly += LANDERSIZE/3 for i in xrange(EXPLODEPARTICLES): a = (float(i) / EXPLODEPARTICLES) * pi * 2 vx = lvx + cos(a) * EXPLODEV vy = lvy + sin(a) * EXPLODEV s['parts'].append(shoot(lx, ly, vx, vy, EXPLODEPARTICLEVAR, EXPLODEPARTICLEVAR, EXPLODEPARTICLEMULT)) s['message'] = reason s['messagecolor1'] = '#ff0000' s['messagecolor2'] = None s['messagecnt'] = 0 s['messagestate'] = 0 def victory(): s['alive'] = False s['win'] = True s['message'] = "WHO IS AWESOME?\\nYOU'RE AWESOME!" s['messagecolor1'] = '#ffff00' s['messagecolor2'] = '#00ff00' s['messagecnt'] = 0 s['messagestate'] = 0 def frame(): print "frame" print "color #000080" print "line 0 0 0", VIEWPORT print "line", VIEWPORT, "0", VIEWPORT, VIEWPORT print "invoke stars" for ((px, py, pvx, pvy), t, size) in s['parts']: gradient(PARTICLEGRADIENT, t) print "alpha %.2f" % ((1.0 - t/2.0),) print "fillcircle %.4f %.4f %.4f %s" % (px, py, size, PARTICLEVERTS) print "alpha 255" print "invoke terrain" if s['alive']: print "color white" print "text right bottom 0", VIEWPORT, "\"%.2f\"" % (s['fuel'],) if abs(s['lander'][2]) > MAXLANDVX or abs(s['lander'][3]) > MAXLANDVY: print "color red" else: print "color green" print "text left bottom", VIEWPORT, VIEWPORT, "\"%+5.2f %+5.2f\"" % (s['lander'][2], s['lander'][3]) if s['drawlander']: drawlander(s['lander']) if s['message']: clr = s['messagecolor1'] if s['messagestate'] == 0 else s['messagecolor2'] if clr is not None: print "color", clr print "text", VIEWPORT/2, VIEWPORT/2, "\"%s\"" % (s['message'],) if not s['alive']: print "color white" print "text center bottom", VIEWPORT/2, VIEWPORT, "\"PRESS SPACE\"" if SHOWFPS and s['fps'] is not None: print "color red" print "text right top 0 0 \"%.2f\"" % (s['fps'],) print "flushdelay 0.0166666" def update(dt): s['fps'] = 1.0/dt if s['alive']: fx=0.0 fy=0.0 if keystate['l'] and s['fuel']>0: fx -= LATERALSTRENGTH s['fuel'] -= (LATERALSTRENGTH * CONSUMPTION * dt) rocketpulse(1, 0, LATPARTICLEPULSE) if keystate['r'] and s['fuel']>0: fx += LATERALSTRENGTH s['fuel'] -= (LATERALSTRENGTH * CONSUMPTION * dt) rocketpulse(-1, 0, LATPARTICLEPULSE) if keystate['u'] and s['fuel']>0: fy += VERTICALSTRENGTH s['fuel'] -= (VERTICALSTRENGTH * CONSUMPTION * dt) rocketpulse(0, -1, VERTPARTICLEPULSE) s['fuel'] = max(0, s['fuel']) s['lander'] = updatepos(dt, s['lander'], fx, fy) (lx, ly, lvx, lvy) = s['lander'] if lx < LANDERSIZE/2: kill("THINK INSIDE THE BOX") elif lx > VIEWPORT - LANDERSIZE/2: kill("THINK INSIDE THE BOX") else: sampleleft=lx-LANDERSIZE/2.8 sampleright=lx+LANDERSIZE/2.8 nsamples=LANDERSIZE*3 step=(sampleright-sampleleft)/nsamples sam=[tersample(sampleleft+i*step) for i in xrange(nsamples+1)] maxs = max(sam) if ly <= maxs: mins = min(sam) vari = maxs - mins if vari > MAXVARI: kill("AREA TOO BUMPY") elif abs(lvx) > MAXLANDVX: kill("TOO FAST SIDEWAYS") elif abs(lvy) > MAXLANDVY: kill("CRATERED") else: s['lander'] = (lx, mins+vari/2, 0, 0) victory() np = [] for (pos, t, size) in s['parts']: pos = updatepos(dt, pos, 0, 0) (px, py, vx, vy) = pos t += dt ty = tersample(px) if t < 1.0 and px > size and px < VIEWPORT - size and py > ty: np.append((pos, t, size)) s['parts'] = np while True: l = sys.stdin.readline().strip().split() if l[0] == 'KEYDOWN': if l[3] in keymap: keystate[keymap[l[3]]] = True elif l[3] == 'escape': return s['win'] elif l[3] == 'space' and not s['alive']: return s['win'] elif l[0] == 'KEYUP': if l[2] in keymap: keystate[keymap[l[2]]] = False elif l[0] == 'FLUSHDELAY': s['messagecnt'] += 1 if s['messagecnt'] >= BLINKRATE: s['messagecnt'] = 0 s['messagestate'] = 1 - s['messagestate'] update(float(l[1])) frame() sys.stdout.flush() print "flushdelay 0" sys.stdout.flush() while True: newstars() genmoon() if not mainmenu(): exit() newstars() newterrain() if game(): NUMVICTORIES += 1 NUMGAMES += 1