summaryrefslogtreecommitdiff
path: root/freebsdtvision/tutorial/tvlife.cc
diff options
context:
space:
mode:
Diffstat (limited to 'freebsdtvision/tutorial/tvlife.cc')
-rw-r--r--freebsdtvision/tutorial/tvlife.cc947
1 files changed, 947 insertions, 0 deletions
diff --git a/freebsdtvision/tutorial/tvlife.cc b/freebsdtvision/tutorial/tvlife.cc
new file mode 100644
index 0000000..d5cf625
--- /dev/null
+++ b/freebsdtvision/tutorial/tvlife.cc
@@ -0,0 +1,947 @@
+/*
+ * TVision example: the classic life example
+ *
+ * Written by Sergio Sigala <sergio@sigala.it>
+ *
+ * The mouse may be used to edit the patterns within the life window.
+ * Clicking with the left button inside the life window will cause a dot to
+ * appear at the cursor location. Clicking with the right mouse button will
+ * remove the dot. This allows the user to create his/her own patterns.
+ *
+ * Modified by Max Okumoto <okumoto@ucsd.edu>
+ */
+
+#define Uses_TApplication
+#define Uses_TButton
+#define Uses_TDeskTop
+#define Uses_TDialog
+#define Uses_TKeys
+#define Uses_TMenuBar
+#define Uses_TMenuItem
+#define Uses_TParamText
+#define Uses_TStatusDef
+#define Uses_TStatusItem
+#define Uses_TStatusLine
+#define Uses_TSubMenu
+#define Uses_TWindow
+#include <tvision/tv.h>
+
+#include <stdlib.h>
+
+enum
+{
+ cmAbout = 101, //about box
+ cmCreate, //creates a new life window
+ cmOneStep, //advance one step
+ cmUpdate, //issued when idle
+ cmStartStop, //starts or stops a life window
+ cmClearBoard, //clears the life board
+ cmRandom, //randomly fills the life board
+ cmPat01, cmPat02, cmPat03, cmPat04, cmPat05,
+ cmPat06, cmPat07, cmPat08, cmPat09, cmPat10,
+ cmPat11, cmPat12, cmPat13, cmPat14, cmPat15,
+ cmPat16, cmPat17, cmPat18, cmPat19, cmPat20,
+ cmPat21, cmPat22, cmPat23, cmPat24, cmPat25,
+ cmPat26, cmPat27, cmPat28, cmPat29
+};
+
+class TLifeInterior: public TView
+{
+ int running;
+ uchar* board;
+public:
+ TLifeInterior(TRect& bounds);
+ ~TLifeInterior();
+ void changeBounds(const TRect& bounds);
+ void clearBoard();
+ void draw();
+ void getPattern(int pat);
+ void handleEvent(TEvent& event);
+ void handleMouse(TEvent& event);
+ void iterateBoard();
+ uchar& map(int x, int y);
+ uchar& map(int x, int y, uchar* work);
+ int present(int x, int y);
+ void randomizeBoard();
+ void setState(ushort aState, Boolean enable);
+};
+
+class TLifeWindow: public TWindow
+{
+ TLifeInterior* life;
+public:
+ static const int minW = 28;
+ static const int minH = 11;
+ TLifeWindow(TRect& bounds, char* str, int num);
+ void sizeLimits(TPoint& min, TPoint& max);
+};
+
+class TMyApp: public TApplication
+{
+ TCommandSet windowCommands;
+public:
+ TMyApp();
+ static TMenuBar* initMenuBar(TRect r);
+ static TStatusLine* initStatusLine(TRect r);
+ void aboutBox();
+ void createLifeWindow();
+ TCommandSet getCommands();
+ void handleEvent(TEvent& event);
+ void idle();
+};
+
+/****************************************************************************
+ *
+ * Patterns for tvlife. Adapted from xlock; original copyright notice below.
+ *
+ * Copyright (c) 1988-91 by Patrick J. Naughton.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *****************************************************************************/
+
+#define NUMPTS 63
+
+/* Patterns have < NUMPTS pts (and should have a size of <= 32x32,
+ the Gun is an exception) */
+
+static int patterns[][2 * NUMPTS + 1] =
+{
+ { /* GLIDER GUN */
+ 6, -4,
+ 5, -3, 6, -3,
+ -6, -2, -5, -2, 8, -2, 9, -2, 16, -2,
+ -7, -1, 8, -1, 9, -1, 10, -1, 16, -1, 17, -1,
+ -18, 0, -17, 0, -8, 0, 8, 0, 9, 1,
+ -17, 1, -8, 1, 5, 1, 6, 1,
+ -8, 2, 6, 2,
+ -7, 3,
+ -6, 4, -5, 4,
+ 127
+ },
+ { /* FIGURE EIGHT */
+ -3, -3, -2, -3, -1, -3,
+ -3, -2, -2, -2, -1, -2,
+ -3, -1, -2, -1, -1, -1,
+ 0, 0, 1, 0, 2, 0,
+ 0, 1, 1, 1, 2, 1,
+ 0, 2, 1, 2, 2, 2,
+ 127
+ },
+ { /* PULSAR */
+ -2, -1, -1, -1, 0, -1, 1, -1, 2, -1,
+ -2, 0, 2, 0,
+ 127
+ },
+ { /* BARBER POLE P2 */
+ -6, -6, -5, -6,
+ -6, -5, -4, -5,
+ -4, -3, -2, -3,
+ -2, -1, 0, -1,
+ 0, 1, 2, 1,
+ 2, 3, 4, 3,
+ 5, 4,
+ 4, 5, 5, 5,
+ 127
+ },
+ { /* ACHIM P5 */
+ -6, -6, -5, -6,
+ -6, -5,
+ -4, -4,
+ -4, -3, -2, -3,
+ -2, -1, 0, -1,
+ 0, 1, 2, 1,
+ 2, 3, 3, 3,
+ 5, 4,
+ 4, 5, 5, 5,
+ 127
+ },
+ { /* HERTZ P4 */
+ -2, -5, -1, -5,
+ -2, -4, -1, -4,
+ -7, -2, -6, -2, -2, -2, -1, -2, 0, -2, 1, -2, 5, -2, 6, -2,
+ -7, -1, -5, -1, -3, -1, 2, -1, 4, -1, 6, -1,
+ -5, 0, -3, 0, -2, 0, 2, 0, 4, 0,
+ -7, 1, -5, 1, -3, 1, 2, 1, 4, 1, 6, 1,
+ -7, 2, -6, 2, -2, 2, -1, 2, 0, 2, 1, 2, 5, 2, 6, 2,
+ -2, 4, -1, 4,
+ -2, 5, -1, 5,
+ 127
+ },
+ { /* TUMBLER */
+ -2, -3, -1, -3, 1, -3, 2, -3,
+ -2, -2, -1, -2, 1, -2, 2, -2,
+ -1, -1, 1, -1,
+ -3, 0, -1, 0, 1, 0, 3, 0,
+ -3, 1, -1, 1, 1, 1, 3, 1,
+ -3, 2, -2, 2, 2, 2, 3, 2,
+ 127
+ },
+ { /* PULSE1 P4 */
+ 0, -3, 1, -3,
+ -2, -2, 0, -2,
+ -3, -1, 3, -1,
+ -2, 0, 2, 0, 3, 0,
+ 0, 2, 2, 2,
+ 1, 3,
+ 127
+ },
+ { /* SHINING FLOWER P5 */
+ -1, -4, 0, -4,
+ -2, -3, 1, -3,
+ -3, -2, 2, -2,
+ -4, -1, 3, -1,
+ -4, 0, 3, 0,
+ -3, 1, 2, 1,
+ -2, 2, 1, 2,
+ -1, 3, 0, 3,
+ 127
+ },
+ { /* PULSE2 P6 */
+ 0, -4, 1, -4,
+ -4, -3, -3, -3, -1, -3,
+ -4, -2, -3, -2, 0, -2, 3, -2,
+ 1, -1, 3, -1,
+ 2, 0,
+ 1, 2, 2, 2,
+ 1, 3, 2, 3,
+ 127
+ },
+ { /* PINWHEEL, CLOCK P4 */
+ -2, -6, -1, -6,
+ -2, -5, -1, -5,
+ -2, -3, -1, -3, 0, -3, 1, -3,
+ -3, -2, -1, -2, 2, -2, 4, -2, 5, -2,
+ -3, -1, 1, -1, 2, -1, 4, -1, 5, -1,
+ -6, 0, -5, 0, -3, 0, 0, 0, 2, 0,
+ -6, 1, -5, 1, -3, 1, 2, 1,
+ -2, 2, -1, 2, 0, 2, 1, 2,
+ 0, 4, 1, 4,
+ 0, 5, 1, 5,
+ 127
+ },
+ { /* PENTADECATHOLON */
+ -5, 0, -4, 0, -3, 0, -2, 0, -1, 0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0,
+ 127
+ },
+ { /* PISTON */
+ 1, -3, 2, -3,
+ 0, -2,
+ -10, -1, -1, -1,
+ -11, 0, -10, 0, -1, 0, 9, 0, 10, 0,
+ -1, 1, 9, 1,
+ 0, 2,
+ 1, 3, 2, 3,
+ 127
+ },
+ { /* PISTON2 */
+ -3, -5,
+ -14, -4, -13, -4, -4, -4, -3, -4, 13, -4, 14, -4,
+ -14, -3, -13, -3, -5, -3, -4, -3, 13, -3, 14, -3,
+ -4, -2, -3, -2, 0, -2, 1, -2,
+ -4, 2, -3, 2, 0, 2, 1, 2,
+ -14, 3, -13, 3, -5, 3, -4, 3, 13, 3, 14, 3,
+ -14, 4, -13, 4, -4, 4, -3, 4, 13, 4, 14, 4,
+ -3, 5,
+ 127
+ },
+ { /* SWITCH ENGINE */
+ -12, -3, -10, -3,
+ -13, -2,
+ -12, -1, -9, -1,
+ -10, 0, -9, 0, -8, 0,
+ 13, 2, 14, 2,
+ 13, 3,
+ 127
+ },
+ { /* GEARS (gear, flywheel, blinker) */
+ -1, -4,
+ -1, -3, 1, -3,
+ -3, -2,
+ 2, -1, 3, -1,
+ -4, 0, -3, 0,
+ 2, 1,
+ -2, 2, 0, 2,
+ 0, 3,
+
+ 5, 3,
+ 3, 4, 4, 4,
+ 5, 5, 6, 5,
+ 4, 6,
+
+ 8, 0,
+ 8, 1,
+ 8, 2,
+ 127
+ },
+ { /* TURBINE8 */
+ -4, -4, -3, -4, -2, -4, -1, -4, 0, -4, 1, -4, 3, -4, 4, -4,
+ -4, -3, -3, -3, -2, -3, -1, -3, 0, -3, 1, -3, 3, -3, 4, -3,
+ 3, -2, 4, -2,
+ -4, -1, -3, -1, 3, -1, 4, -1,
+ -4, 0, -3, 0, 3, 0, 4, 0,
+ -4, 1, -3, 1, 3, 1, 4, 1,
+ -4, 2, -3, 2,
+ -4, 3, -3, 3, -1, 3, 0, 3, 1, 3, 2, 3, 3, 3, 4, 3,
+ -4, 4, -3, 4, -1, 4, 0, 4, 1, 4, 2, 4, 3, 4, 4, 4,
+ 127
+ },
+ { /* P16 */
+ -3, -6, 1, -6, 2, -6,
+ -3, -5, 0, -5, 3, -5,
+ 3, -4,
+ -5, -3, -4, -3, 1, -3, 2, -3, 5, -3, 6, -3,
+ -6, -2, -3, -2,
+ -6, -1, -3, -1,
+ -5, 0, 5, 0,
+ 3, 1, 6, 1,
+ 3, 2, 6, 2,
+ -6, 3, -5, 3, -2, 3, -1, 3, 4, 3, 5, 3,
+ -3, 4,
+ -3, 5, 0, 5, 3, 5,
+ -2, 6, -1, 6, 3, 6,
+ 127
+ },
+ { /* PUFFER */
+ 1, -9,
+ 2, -8,
+ -2, -7, 2, -7,
+ -1, -6, 0, -6, 1, -6, 2, -6,
+ -2, -2,
+ -1, -1, 0, -1,
+ 0, 0,
+ 0, 1,
+ -1, 2,
+ 1, 5,
+ 2, 6,
+ -2, 7, 2, 7,
+ -1, 8, 0, 8, 1, 8, 2, 8,
+ 127
+ },
+ { /* ESCORT */
+ 3, -8,
+ 4, -7,
+ -2, -6, 4, -6,
+ -1, -5, 0, -5, 1, -5, 2, -5, 3, -5, 4, -5,
+ -5, -1, -4, -1, -3, -1, -2, -1, -1, -1, 0, -1,
+ 1, -1, 2, -1, 3, -1, 4, -1, 5, -1, 6, -1,
+ -6, 0, 6, 0,
+ 6, 1,
+ 5, 2,
+ 3, 4,
+ 4, 5,
+ -2, 6, 4, 6,
+ -1, 7, 0, 7, 1, 7, 2, 7, 3, 7, 4, 7,
+ 127
+ },
+ { /* DART SPEED 1/3 */
+ 3, -7,
+ 2, -6, 4, -6,
+ 1, -5, 2, -5,
+ 4, -4,
+ 0, -3, 4, -3,
+ -3, -2, 0, -2,
+ -4, -1, -2, -1, 1, -1, 2, -1, 3, -1, 4, -1,
+ -5, 0, -2, 0,
+ -4, 1, -2, 1, 1, 1, 2, 1, 3, 1, 4, 1,
+ -3, 2, 0, 2,
+ 0, 3, 4, 3,
+ 4, 4,
+ 1, 5, 2, 5,
+ 2, 6, 4, 6,
+ 3, 7,
+ 127
+ },
+ { /* PERIOD 4 SPEED 1/2 */
+ -3, -5,
+ -4, -4, -3, -4, -2, -4, -1, -4, 0, -4,
+ -5, -3, -4, -3, 0, -3, 1, -3, 3, -3,
+ -4, -2, 4, -2,
+ -3, -1, -2, -1, 1, -1, 3, -1,
+ -3, 1, -2, 1, 1, 1, 3, 1,
+ -4, 2, 4, 2,
+ -5, 3, -4, 3, 0, 3, 1, 3, 3, 3,
+ -4, 4, -3, 4, -2, 4, -1, 4, 0, 4,
+ -3, 5,
+ 127
+ },
+ { /* ANOTHER PERIOD 4 SPEED 1/2 */
+ -4, -7, -3, -7, -1, -7, 0, -7, 1, -7, 2, -7, 3, -7, 4, -7,
+ -5, -6, -4, -6, -3, -6, -2, -6, 5, -6,
+ -6, -5, -5, -5,
+ -5, -4, 5, -4,
+ -4, -3, -3, -3, -2, -3, 0, -3,
+ -2, -2,
+ -2, -1,
+ -1, 0,
+ -2, 1,
+ -2, 2,
+ -4, 3, -3, 3, -2, 3, 0, 3,
+ -5, 4, 5, 4,
+ -6, 5, -5, 5,
+ -5, 6, -4, 6, -3, 6, -2, 6, 5, 6,
+ -4, 7, -3, 7, -1, 7, 0, 7, 1, 7, 2, 7, 3, 7, 4, 7,
+ 127
+ },
+ { /* SMALLEST KNOWN PERIOD 3 SPACESHIP SPEED 1/3 */
+ 0, -8,
+ -1, -7, 1, -7,
+ -1, -6, 1, -6,
+ -1, -5,
+ -2, -3, -1, -3,
+ -1, -2, 1, -2,
+ -2, -1, 0, -1,
+ -2, 0, -1, 0, 0, 0,
+ -1, 2, 1, 2,
+ -1, 3, 0, 3,
+ 0, 4,
+ 0, 5, 2, 5,
+ 0, 6, 2, 6,
+ 1, 7,
+ 127
+ },
+ { /* TURTLE SPEED 1/3 */
+ -4, -5, -3, -5, -2, -5, 6, -5,
+ -4, -4, -3, -4, 0, -4, 2, -4, 3, -4, 5, -4, 6, -4,
+ -2, -3, -1, -3, 0, -3, 5, -3,
+ -4, -2, -1, -2, 1, -2, 5, -2,
+ -5, -1, 0, -1, 5, -1,
+ -5, 0, 0, 0, 5, 0,
+ -4, 1, -1, 1, 1, 1, 5, 1,
+ -2, 2, -1, 2, 0, 2, 5, 2,
+ -4, 3, -3, 3, 0, 3, 2, 3, 3, 3, 5, 3, 6, 3,
+ -4, 4, -3, 4, -2, 4, 6, 4,
+ 127
+ },
+ { /* SMALLEST KNOWN PERIOD 5 SPEED 2/5 */
+ 1, -7, 3, -7,
+ -2, -6, 3, -6,
+ -3, -5, -2, -5, -1, -5, 4, -5,
+ -4, -4, -2, -4,
+ -5, -3, -4, -3, -1, -3, 0, -3, 5, -3,
+ -4, -2, -3, -2, 0, -2, 1, -2, 2, -2, 3, -2, 4, -2,
+ -4, 2, -3, 2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2,
+ -5, 3, -4, 3, -1, 3, 0, 3, 5, 3,
+ -4, 4, -2, 4,
+ -3, 5, -2, 5, -1, 5, 4, 5,
+ -2, 6, 3, 6,
+ 1, 7, 3, 7,
+ 127
+ },
+ { /* SYM PUFFER */
+ 1, -4, 2, -4, 3, -4, 4, -4,
+ 0, -3, 4, -3,
+ 4, -2,
+ -4, -1, -3, -1, 0, -1, 3, -1,
+ -4, 0, -3, 0, -2, 0,
+ -4, 1, -3, 1, 0, 1, 3, 1,
+ 4, 2,
+ 0, 3, 4, 3,
+ 1, 4, 2, 4, 3, 4, 4, 4,
+ 127
+ },
+ { /* ], NEAR SHIP, PI HEPTOMINO */
+ -2, -1, -1, -1, 0, -1,
+ 1, 0,
+ -2, 1, -1, 1, 0, 1,
+ 127
+ },
+ { /* R PENTOMINO */
+ 0, -1, 1, -1,
+ -1, 0, 0, 0,
+ 0, 1,
+ 127
+ }
+};
+
+#define NPATS (sizeof patterns / sizeof patterns[0])
+
+TLifeInterior::TLifeInterior(TRect& bounds)
+ : TView(bounds), running(0), board(NULL)
+{
+ eventMask = evMouseDown | evKeyDown | evCommand | evBroadcast;
+ growMode = gfGrowHiX | gfGrowHiY;
+ options |= ofSelectable;
+
+ board = new uchar[size.x * size.y]; //get memory
+ getPattern(0);
+}
+
+TLifeInterior::~TLifeInterior()
+{
+ if (board != 0) delete[] board; //release memory
+}
+
+void TLifeInterior::changeBounds(const TRect& bounds)
+{
+ if (board == 0) return; //exit if no board
+
+ TPoint oldSize = size; //calculate sizes
+ TPoint newSize = bounds.b - bounds.a;
+
+ uchar* work = new uchar[newSize.x * newSize.y]; //get memory
+ if (work == 0) return;
+
+ //clear board
+
+ for (int i = 0; i < newSize.x * newSize.y; i++)
+ work[i] = 0;
+
+ //copy
+
+ for (int y = 0; y < min(oldSize.y, newSize.y); y++)
+ for (int x = 0; x < min(oldSize.x, newSize.x); x++)
+ {
+ uchar *from = &board[oldSize.x * y + x];
+ uchar *to = &work[newSize.x * y + x ];
+
+ *to = *from; //copy a cell
+ }
+ uchar* old = board; //swap buffers
+ board = work;
+ delete[] old;
+
+ TView::changeBounds(bounds); //update size and redraw
+}
+
+void TLifeInterior::clearBoard()
+{
+ if (board == 0) return; //exit if no board
+
+ //clear board
+
+ for (int i = 0; i < size.x * size.y; i++)
+ board[i] = 0;
+}
+
+void TLifeInterior::draw()
+{
+ uchar color = running ? 0x4f : 0x1f;
+ uchar* from = board;
+
+#if 1
+ int i = size.x * size.y;
+ ushort *buf = new ushort[i];
+ ushort *to = buf;
+
+ while (i-- > 0)
+ {
+ *to = (*from++ != 0) ? '*' : ' ';
+ *to |= (color << 8);
+ to++;
+ }
+ writeBuf(0, 0, size.x, size.y, buf);
+ delete[] buf;
+#else
+ TDrawBuffer b;
+ b.moveChar(0, ' ', color, size.x);
+
+ for (int i = 0; i < size.y; i++)
+ {
+ TDrawBuffer d = b;
+
+ if (from != 0)
+ {
+ for (int j = 0; j < size.x; j++)
+ {
+ if (*from != 0) d.putChar(j, '*');
+ from++;
+ }
+ }
+ writeLine(0, i, size.x, 1, d);
+ }
+#endif
+}
+
+void TLifeInterior::getPattern(int pat)
+{
+ if (board == 0) return; //exit if no board
+ clearBoard();
+
+ int i = pat % NPATS;
+ int *patptr = &patterns[i][0];
+ int col;
+
+ while ((col = *patptr++) != 127)
+ {
+ int row = *patptr++;
+
+ col += size.x / 2;
+ row += size.y / 2;
+ map(col, row) = 1;
+ }
+ drawView();
+}
+
+void TLifeInterior::handleEvent(TEvent& event)
+{
+ TView::handleEvent(event);
+ switch (event.what)
+ {
+ case evBroadcast:
+ if (event.message.command == cmUpdate && running)
+ {
+ iterateBoard();
+ drawView();
+ }
+ break;
+ case evCommand:
+ if (event.message.command >= cmPat01 &&
+ event.message.command < cmPat01 + NPATS)
+ {
+ getPattern(event.message.command - cmPat01);
+ }
+ else switch (event.message.command)
+ {
+ case cmOneStep:
+ iterateBoard();
+ drawView();
+ break;
+ case cmClearBoard:
+ clearBoard();
+ drawView();
+ break;
+ case cmRandom:
+ randomizeBoard();
+ drawView();
+ break;
+ case cmStartStop:
+ running = 1 - running;
+ drawView();
+ break;
+ default:
+ return;
+ }
+ clearEvent(event);
+ break;
+ case evMouseDown:
+ handleMouse(event);
+ }
+}
+
+void TLifeInterior::handleMouse(TEvent& event)
+{
+ TRect clickRect = getExtent();
+ do
+ {
+ TPoint mouse = makeLocal(event.mouse.where);
+ if (clickRect.contains(mouse))
+ {
+ if ((event.mouse.buttons & mbLeftButton) != 0)
+ map(mouse.x, mouse.y) = 1;
+ if ((event.mouse.buttons & mbRightButton) != 0)
+ map(mouse.x, mouse.y) = 0;
+ drawView();
+ }
+ }
+ while (mouseEvent(event, evMouseMove));
+ clearEvent(event);
+}
+
+void TLifeInterior::iterateBoard()
+{
+ if (board == 0) return; //exit if no board
+
+ uchar* work = new uchar[size.x * size.y]; //get memory
+ if (work == 0) return;
+
+ //clear board
+
+ for (int i = 0; i < size.x * size.y; i++)
+ work[i] = 0;
+
+ int differences = 0;
+
+ //iterate
+
+ for (int y = 0; y < size.y; y++)
+ for (int x = 0; x < size.x; x++)
+ {
+ //find number of neighbors
+
+ int n = present(x-1, y-1) + present(x, y-1) +
+ present(x+1, y-1) + present(x-1, y) +
+ present(x+1, y) + present(x-1, y+1) +
+ present(x, y+1) + present(x+1, y+1);
+
+ if (map(x, y) == 1)
+ {
+ if (n == 2 || n == 3) map(x, y, work) = 1;
+ }
+ if (map(x, y) == 0)
+ {
+ if (n == 3) map(x, y, work) = 1;
+ }
+ if (map(x, y) != map(x, y, work)) differences++;
+ }
+ uchar* old = board; //swap buffers
+ board = work;
+ delete[] old;
+
+ if (differences == 0) running = 0;
+}
+
+uchar& TLifeInterior::map(int x, int y)
+{
+ return board[size.x * y + x];
+}
+
+uchar& TLifeInterior::map(int x, int y, uchar* work)
+{
+ return work[size.x * y + x];
+}
+
+int TLifeInterior::present(int x, int y)
+{
+ //exit if the position is outside the rectangle
+
+ if (x < 0 || x >= size.x || y < 0 || y >= size.y) return 0;
+ if (board[size.x * y + x] == 0) return 0;
+ return 1;
+}
+
+inline int range(int test, int min, int max)
+{
+ return test < min ? min : test > max ? max : test;
+}
+
+void TLifeInterior::randomizeBoard()
+{
+ if (board == 0) return; //exit if no board
+ clearBoard();
+
+ //insert random cells
+
+ int howMany = size.x * size.y / 5;
+ for (int i = 0; i < howMany; i++)
+ {
+ int x, y;
+
+ do
+ {
+ x = (int) (((double) rand() * size.x) / RAND_MAX);
+ y = (int) (((double) rand() * size.y) / RAND_MAX);
+
+ x = range(0, x, size.x - 1);
+ y = range(0, y, size.y - 1);
+ }
+ while (map(x, y) != 0);
+ map(x, y) = 1;
+ }
+}
+
+void TLifeInterior::setState(ushort aState, Boolean enable)
+{
+ TView::setState(aState, enable);
+/* TCommandSet windowCommands;
+ if ((aState & sfFocused) != 0)
+ {
+ if (running) windowCommands += cmStop;
+ else windowCommands += cmStart;
+ if (enable != False) enableCommands(windowCommands);
+ else disableCommands(windowCommands);
+ }*/
+}
+
+TLifeWindow::TLifeWindow(TRect& bounds, char* str, int num):
+ TWindow(bounds, str, num), TWindowInit(&TLifeWindow::initFrame)
+{
+ options |= ofFirstClick | ofTileable;
+
+ TRect r = getClipRect();
+ r.grow(-1, -1);
+ insert(life = new TLifeInterior(r));
+}
+
+void TLifeWindow::sizeLimits(TPoint &min, TPoint &max)
+{
+ TView::sizeLimits(min, max);
+ min.x = minW;
+ min.y = minH;
+}
+
+TMyApp::TMyApp(): TProgInit(&TMyApp::initStatusLine, &TMyApp::initMenuBar,
+ &TMyApp::initDeskTop), windowCommands(getCommands())
+{
+}
+
+void TMyApp::aboutBox()
+{
+ TDialog *box = new TDialog(TRect(0, 0, 32, 10), "About");
+ box->insert(new TStaticText(TRect(1, 2, 1+30, 2+5),
+ "\003TVLIFE\n"
+ "\003The classic life example\n\n"
+ "\003Written by Sergio Sigala"));
+ box->insert(new TButton(TRect(11, 7, 11+10, 9), "O~K~", cmOK,
+ bfDefault));
+ box->options |= ofCentered;
+ executeDialog(box);
+}
+
+void TMyApp::createLifeWindow()
+{
+// TRect r(0, 0, TLifeWindow::minW, TLifeWindow::minH);
+// r.move(random() % 53, random() % 16); //randomly move around screen
+ TRect r;
+ r = deskTop->getExtent();
+ r.grow(-1, -1);
+
+ TView *w = validView(new TLifeWindow(r, "Life", 0));
+ if (w != 0) deskTop->insert(w);
+}
+
+TCommandSet TMyApp::getCommands()
+{
+ TCommandSet wc;
+
+ wc += cmCascade; //add window commands
+ wc += cmClearBoard;
+ wc += cmOneStep;
+ wc += cmRandom;
+ wc += cmStartStop;
+ wc += cmTile;
+ for (uint i = 0; i < NPATS; i++) //scan pattern commands
+ {
+ wc += cmPat01 + i; //add pattern commands
+ }
+ return wc;
+}
+
+void TMyApp::handleEvent(TEvent &event)
+{
+ TApplication::handleEvent(event);
+ if (event.what == evCommand)
+ {
+ switch (event.message.command)
+ {
+ case cmAbout:
+ aboutBox();
+ break;
+ case cmCascade:
+ deskTop->cascade(deskTop->getExtent());
+ break;
+ case cmCreate:
+ createLifeWindow();
+ break;
+ case cmTile:
+ deskTop->tile(deskTop->getExtent());
+ break;
+ default:
+ return;
+ }
+ clearEvent(event);
+ }
+}
+
+static Boolean isTileable(TView *p, void *)
+{
+ if ((p->options & ofTileable) != 0) return True;
+ else return False;
+}
+
+void TMyApp::idle()
+{
+ TApplication::idle();
+ if (deskTop->firstThat(isTileable, 0) != 0)
+ enableCommands(windowCommands);
+ else disableCommands(windowCommands);
+ message(deskTop, evBroadcast, cmUpdate, 0);
+}
+
+#define ITEM(name, comm) *new TMenuItem(name, comm, kbNoKey, hcNoContext)
+
+TMenuBar* TMyApp::initMenuBar(TRect r)
+{
+ TSubMenu& sub1 = *new TSubMenu("~\360~", 0, hcNoContext) +
+ *new TMenuItem("~A~bout...", cmAbout, kbNoKey, hcNoContext);
+
+ TSubMenu& sub2 = *new TSubMenu("~F~ile", 0) +
+ *new TMenuItem("~L~ife window", cmCreate, kbF9, hcNoContext, "F9") +
+ newLine() +
+ *new TMenuItem("E~x~it", cmQuit, kbAltX, hcNoContext, "Alt-X");
+
+ TSubMenu& sub3 = *new TSubMenu("~A~ction", 0) +
+ *new TMenuItem("~C~lear", cmClearBoard, kbNoKey, hcNoContext) +
+ *new TMenuItem("~O~ne step", cmOneStep, kbNoKey, hcNoContext) +
+ *new TMenuItem("~R~andomize", cmRandom, kbNoKey, hcNoContext) +
+ *new TMenuItem("~S~tart/Stop", cmStartStop, kbNoKey, hcNoContext);
+
+ TSubMenu& sub4 = *new TSubMenu("~W~indow", 0) +
+ *new TMenuItem("~S~ize/move", cmResize, kbCtrlF5, hcNoContext, "Ctrl-F5") +
+ *new TMenuItem("~Z~oom", cmZoom, kbF5, hcNoContext, "F5") +
+ *new TMenuItem("~N~ext", cmNext, kbF6, hcNoContext, "F6") +
+ *new TMenuItem("~P~revious", cmPrev, kbShiftF6, hcNoContext, "Shift-F6") +
+ *new TMenuItem("~C~lose", cmClose, kbAltF3, hcNoContext, "Alt-F3") +
+ newLine() +
+ *new TMenuItem("~T~ile", cmTile, kbNoKey, hcNoContext) +
+ *new TMenuItem("C~a~scade", cmCascade, kbNoKey, hcNoContext);
+
+ TSubMenu& sub5 = *new TSubMenu("Patterns ~1~", 0) +
+ ITEM("Glider Gun", cmPat01) +
+ ITEM("Figure Eight", cmPat02) +
+ ITEM("Pulsar", cmPat03) +
+ ITEM("Barber Pole P2", cmPat04) +
+ ITEM("Achim P5", cmPat05) +
+ ITEM("Hertz P4", cmPat06) +
+ ITEM("Tumbler", cmPat07) +
+ ITEM("Pulse1 P4", cmPat08) +
+ ITEM("Shining Flower P5", cmPat09) +
+ ITEM("Pulse2 P6", cmPat10) +
+ ITEM("Pinwheel, Clock P4", cmPat11) +
+ ITEM("Pentadecatholon", cmPat12) +
+ ITEM("Piston", cmPat13) +
+ ITEM("Piston2", cmPat14) +
+ ITEM("Switch Engine", cmPat15);
+
+ TSubMenu& sub6 = *new TSubMenu("Patterns ~2~", 0) +
+ ITEM("Gears (Gear, Flywheel, Blinker)", cmPat16) +
+ ITEM("Turbine8", cmPat17) +
+ ITEM("P16", cmPat18) +
+ ITEM("Puffer", cmPat19) +
+ ITEM("Escort", cmPat20) +
+ ITEM("Dart Speed 1/3", cmPat21) +
+ ITEM("Period 4 Speed 1/2", cmPat22) +
+ ITEM("Another Period 4 Speed 1/2", cmPat23) +
+ ITEM("Smallest Known Period 3 Spaceship Speed 1/3", cmPat24) +
+ ITEM("Turtle Speed 1/3", cmPat25) +
+ ITEM("Smallest Known Period 5 Speed 2/5", cmPat26) +
+ ITEM("Sym Puffer", cmPat27) +
+ ITEM("], Near Ship, Pi Heptomino", cmPat28) +
+ ITEM("R Pentomino", cmPat29);
+
+ r.b.y = r.a.y + 1;
+ return new TMenuBar(r, sub1 + sub2 + sub3 + sub4 + sub5 + sub6);
+}
+
+TStatusLine* TMyApp::initStatusLine(TRect r)
+{
+ r.a.y = r.b.y - 1;
+ return new TStatusLine(r,
+ *new TStatusDef(0, 50) +
+ *new TStatusItem("~Alt-X~ Exit", kbAltX, cmQuit) +
+ *new TStatusItem("~F9~ Life Window", kbF9, cmCreate) +
+ *new TStatusItem("~Alt-F3~ Close", kbAltF3, cmClose) +
+ *new TStatusItem("One step", kbNoKey, cmOneStep) +
+ *new TStatusItem("Randomize", kbNoKey, cmRandom) +
+ *new TStatusItem("Start/Stop", kbNoKey, cmStartStop) +
+ *new TStatusItem(0, kbF10, cmMenu) +
+ *new TStatusItem(0, kbF5, cmZoom) +
+ *new TStatusItem(0, kbCtrlF5, cmResize));
+}
+
+int main()
+{
+ TMyApp a;
+
+ a.run();
+ return 0;
+}