├── source ├── sym0.bmp ├── sym1.bmp ├── sym2.bmp ├── sym3.bmp ├── sym4.bmp ├── sym5.bmp ├── sym6.bmp ├── sym7.bmp ├── sym8.bmp ├── sym9.bmp ├── wls.ico ├── trans0.bmp ├── trans1.bmp ├── trans2.bmp ├── trans3.bmp ├── trans4.bmp ├── trans5.bmp ├── trans6.bmp ├── trans7.bmp ├── wls-config.h ├── resource.h ├── lifesrc.h ├── wls.rc ├── interact.c └── search.c ├── .gitignore ├── readme.md ├── scripts ├── wls2008.sln └── wls2008.vcproj └── wls.txt /source/sym0.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/sym0.bmp -------------------------------------------------------------------------------- /source/sym1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/sym1.bmp -------------------------------------------------------------------------------- /source/sym2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/sym2.bmp -------------------------------------------------------------------------------- /source/sym3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/sym3.bmp -------------------------------------------------------------------------------- /source/sym4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/sym4.bmp -------------------------------------------------------------------------------- /source/sym5.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/sym5.bmp -------------------------------------------------------------------------------- /source/sym6.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/sym6.bmp -------------------------------------------------------------------------------- /source/sym7.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/sym7.bmp -------------------------------------------------------------------------------- /source/sym8.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/sym8.bmp -------------------------------------------------------------------------------- /source/sym9.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/sym9.bmp -------------------------------------------------------------------------------- /source/wls.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/wls.ico -------------------------------------------------------------------------------- /source/trans0.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/trans0.bmp -------------------------------------------------------------------------------- /source/trans1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/trans1.bmp -------------------------------------------------------------------------------- /source/trans2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/trans2.bmp -------------------------------------------------------------------------------- /source/trans3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/trans3.bmp -------------------------------------------------------------------------------- /source/trans4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/trans4.bmp -------------------------------------------------------------------------------- /source/trans5.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/trans5.bmp -------------------------------------------------------------------------------- /source/trans6.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/trans6.bmp -------------------------------------------------------------------------------- /source/trans7.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsummers/winlifesearch/HEAD/source/trans7.bmp -------------------------------------------------------------------------------- /source/wls-config.h: -------------------------------------------------------------------------------- 1 | // wls-config.h 2 | 3 | // The inimum version of Windows we support: 4 | #define _WIN32_WINNT 0x0501 // 0x0501=WinXP 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /scripts/Debug[JK] 2 | /scripts/Release[JK] 3 | /scripts/Debug[JK]64 4 | /scripts/Release[JK]64 5 | /scripts/wls2008.vcproj.*.user 6 | /scripts/*.ncb 7 | /scripts/*.suo 8 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | WinLifeSearch 2 | ============= 3 | 4 | This is a Windows program to search for oscillators and spaceships (and 5 | similar objects) in Conway's Game of Life (and similar cellular automata). 6 | 7 | See wls.txt for instructions. 8 | 9 | [Web site](http://entropymine.com/wls/) 10 | 11 | -------------------------------------------------------------------------------- /scripts/wls2008.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual C++ Express 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wls", "wls2008.vcproj", "{BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | DebugJ|Win32 = DebugJ|Win32 9 | DebugJ|x64 = DebugJ|x64 10 | DebugK|Win32 = DebugK|Win32 11 | DebugK|x64 = DebugK|x64 12 | ReleaseJ|Win32 = ReleaseJ|Win32 13 | ReleaseJ|x64 = ReleaseJ|x64 14 | ReleaseK|Win32 = ReleaseK|Win32 15 | ReleaseK|x64 = ReleaseK|x64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.DebugJ|Win32.ActiveCfg = DebugJ|Win32 19 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.DebugJ|Win32.Build.0 = DebugJ|Win32 20 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.DebugJ|x64.ActiveCfg = DebugJ|x64 21 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.DebugJ|x64.Build.0 = DebugJ|x64 22 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.DebugK|Win32.ActiveCfg = DebugK|Win32 23 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.DebugK|Win32.Build.0 = DebugK|Win32 24 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.DebugK|x64.ActiveCfg = DebugK|x64 25 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.DebugK|x64.Build.0 = DebugK|x64 26 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.ReleaseJ|Win32.ActiveCfg = ReleaseJ|Win32 27 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.ReleaseJ|Win32.Build.0 = ReleaseJ|Win32 28 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.ReleaseJ|x64.ActiveCfg = ReleaseJ|x64 29 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.ReleaseJ|x64.Build.0 = ReleaseJ|x64 30 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.ReleaseK|Win32.ActiveCfg = ReleaseK|Win32 31 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.ReleaseK|Win32.Build.0 = ReleaseK|Win32 32 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.ReleaseK|x64.ActiveCfg = ReleaseK|x64 33 | {BDDDB68C-40DC-4CB4-8CA9-FCCB802D0321}.ReleaseK|x64.Build.0 = ReleaseK|x64 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /wls.txt: -------------------------------------------------------------------------------- 1 | WinLifeSearch (WLS) 2 | http://entropymine.com/wls/ 3 | 4 | A program to search for oscillators, spaceships, and related objects in 5 | Conway's Game of Life and related cellular automata. 6 | 7 | By Jason Summers 8 | v0.71 - Sep. 2012 9 | 10 | Other authors: David Bell, Karel Suhajda 11 | 12 | Based on LIFESRC v3.5 by David Bell. 13 | 14 | Requires Windows XP or higher. 15 | 16 | 17 | Brief instructions 18 | ------------------ 19 | 20 | Sorry, but this program is not fully documented. For some things, you may have 21 | to refer to the documentation of LIFESRC, and/or of WinLifeSearch v0.4 and 22 | v0.61. 23 | 24 | There are two variants of WinLifeSearch included in this package: 25 | WinLifeSearchK and WinLifeSearchJ. "K" is based on v0.61, and "J" is based on 26 | v0.4: 27 | 28 | v0.5 --- v0.6 --- v0.61 --- WinLifeSearchK v0.70+ (wls.exe) 29 | / 30 | ... --- v0.4 ------------------------ WinLifeSearchJ v0.70+ (wlsj.exe) 31 | 32 | In most respects, "K" is better, but there may be a few reasons to use "J" 33 | (the "fast symmetry" feature in particular). "J" is offered because I want to 34 | be sure that the old 0.4 version is completely obsolete. 35 | 36 | To run, open wls.exe or wlsj.exe. 37 | 38 | Keyboard/mouse functions: 39 | 's' or left mouse button = The cell under the mouse is forced ON. 40 | 'a' or right mouse button = The cell under the mouse is forced OFF. 41 | 'x' = Cell is UNCHECKED. Use this to find partial patterns. 42 | 'f' = Cell is frozen (forced to be the same in all generations. Frozen 43 | cells don't seem to work too well sometimes. There may be a bug 44 | somewhere causing this. 45 | 'c' = Cell is cleared. 46 | 'S' or shift+left mouse button = cell is forced ON in all generations 47 | 'A' or shift+right = cell is forced off in all generations 48 | 'X' = UNCHECKED in all gens 49 | 'C' = cleared in all generations. 50 | Esc = Pause a search 51 | Space = Continue a paused search 52 | Ctrl+R = Reset the search 53 | 54 | You can also select a region of cells using the mouse, then point to the 55 | selected region and press one of the above keys to set many cells at once. 56 | 57 | Set up a search by choosing field size, period, symmetry, translation, etc. 58 | 59 | Force some cells ON. Often just one in one of the leftmost columns is best. 60 | Or, use the Search setting "Force an ON cell in column..." to do that. 61 | 62 | Start the search. Observe partial results. Run simpler searches for 63 | practice. Do lots of waiting. Try to get a feel for how well a search is 64 | going. The "rows" setting will usually determine how fast the search will 65 | run. Increasing the rows by 1 could slow down the search considerably. 66 | Increasing columns has usually little or no effect. 67 | 68 | The fastest direction to search for spaceships is usually to set the X 69 | translation to -1 (or -2 as appropriate). 70 | 71 | 72 | Other notes: 73 | ------------ 74 | 75 | Cells that are the same in all generations will be drawn with a thin border. 76 | At the lower left is a control that shows which generation being displayed. 77 | 78 | You can access the menus while a search is running, but if you change 79 | settings at that time, it will probably screw up the search. However, it is 80 | safe to change the Output settings (I think). 81 | 82 | You can use Edit|Copy to copy the currently displayed pattern to the 83 | clipboard. The format is compatible with most other applications. This works 84 | at any time, even while a search is running. 85 | 86 | Some symmetry and translation settings will be unavailable unless you set 87 | rows and columns to the same number. 88 | 89 | The Save formats from different versions of WLS and LIFESRC are NOT 90 | compatible. 91 | 92 | ------------------------------- 93 | 94 | The C source code is included, along with project files for Visual Studio 2008. 95 | 96 | Warning: The code is quite a mess. I've been trying to clean it up, but I don't 97 | know how far I'll get. 98 | 99 | ------------------------------- 100 | 101 | The licensing terms for this code are not completely clear, but because of 102 | explicit and implicit permissions given by the authors, I believe I can safely 103 | release it under and MIT-style license. 104 | 105 | 106 | Copyright (c) 2002,2012 Jason Summers 107 | Copyright (c) 2003 Karel Suhajda 108 | Copyright (c) 1997 David Bell 109 | 110 | Permission is hereby granted, free of charge, to any person obtaining a copy 111 | of this software and associated documentation files (the "Software"), to deal 112 | in the Software without restriction, including without limitation the rights 113 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 114 | copies of the Software, and to permit persons to whom the Software is 115 | furnished to do so, subject to the following conditions: 116 | 117 | The above copyright notice and this permission notice shall be included in 118 | all copies or substantial portions of the Software. 119 | 120 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 121 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 122 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 123 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 124 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 125 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 126 | THE SOFTWARE. 127 | 128 | ------------------------------- 129 | 130 | -------------------------------------------------------------------------------- /source/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Developer Studio generated include file. 3 | // Used by wls.rc 4 | // 5 | #define IDC_STATIC -1 6 | #define IDB_SYM0 105 7 | #define IDB_SYM1 106 8 | #define IDB_SYM2 107 9 | #define IDB_SYM3 108 10 | #define IDB_SYM4 109 11 | #define IDB_SYM5 110 12 | #define IDB_SYM6 111 13 | #define IDB_SYM7 112 14 | #define IDB_SYM8 113 15 | #define IDB_SYM9 114 16 | #define IDB_TRANS0 116 17 | #define IDB_TRANS1 118 18 | #define IDB_TRANS2 119 19 | #define IDB_TRANS3 120 20 | #define IDB_TRANS4 121 21 | #define IDB_TRANS5 122 22 | #define IDB_TRANS6 123 23 | #define IDB_TRANS7 124 24 | #define IDC_SYM0 1000 25 | #define IDC_SYM1 1001 26 | #define IDC_SYM2 1002 27 | #define IDC_SYM3 1003 28 | #define IDC_SYM4 1004 29 | #define IDC_SYM5 1005 30 | #define IDC_SYM6 1006 31 | #define IDC_SYM7 1007 32 | #define IDC_SYM8 1008 33 | #define IDC_SYM9 1009 34 | #define IDC_ABOUTTEXT 1010 35 | #define IDC_COLUMNS 1015 36 | #define IDC_TRANS0 1016 37 | #define IDC_TRANSX 1017 38 | #define IDC_TRANSY 1018 39 | #define IDC_TRANS1 1019 40 | #define IDC_TRANS2 1020 41 | #define IDC_TRANS3 1021 42 | #define IDC_TRANS4 1022 43 | #define IDC_TRANS5 1023 44 | #define IDC_TRANS6 1024 45 | #define IDC_TRANS7 1025 46 | #define IDC_PERIOD 1029 47 | #define IDC_PERIODTEXT 1030 48 | #define IDC_ROWS 1031 49 | #define IDC_USEROW 1033 50 | #define IDC_RULESTRING 1034 51 | #define IDC_DUMPFILE 1035 52 | #define IDC_OUTPUTFILE 1036 53 | #define IDC_USECOL 1038 54 | #define IDC_ALLOBJECTS 1039 55 | #define IDC_PARENT 1040 56 | #define IDC_CONWAY 1041 57 | #define IDC_STOPONFOUND 1042 58 | #define IDC_OUTPUTFILEYN 1043 59 | #define IDC_MAXCOUNT 1044 60 | #define IDC_OUTPUTALLGEN 1045 61 | #define IDC_COLCELLS 1047 62 | #define IDC_COLWIDTH 1049 63 | #define IDC_NEARCOLS 1051 64 | #define IDC_FOLLOW 1052 65 | #define IDC_FOLLOWGENS 1053 66 | #define IDC_SMART 1054 67 | #define IDC_DUMPFREQ 1055 68 | #define IDC_SMARTTHRESHOLD 1056 69 | #define IDC_VIEWFREQ 1057 70 | #define IDC_SORTORDER 1058 71 | #define IDC_ORDERWIDE 1059 72 | #define IDC_ORDERGENS 1060 73 | #define IDC_OUTPUTCOLS 1062 74 | #define IDC_SMARTWINDOWSTAT 1063 75 | #define IDC_SMARTTHRESHOLDSTAT 1064 76 | #define IDC_FASTSYM 1065 77 | #define IDC_CELLSIZE 1066 78 | #define IDC_COMBINE 1070 79 | #define IDC_STOPONSTEP 1071 80 | #define IDC_SMARTWINDOW 1072 81 | #define IDC_SMARTON 1073 82 | #define IDC_DEFAULTPERIOD 1077 83 | #define IDC_DEFAULTCOLUMNS 1078 84 | #define IDC_DEFAULTROWS 1079 85 | #define IDC_SEARCHPRIORITY 1080 86 | #define IDC_WHEELGENDIR 1081 87 | #define IDC_ICWTEXT 1082 88 | #define IDC_ICTTEXT 1083 89 | #define IDC_SAVESTATEFOLDER 1084 90 | #define IDC_SAVESTATEBROWSE 1085 91 | #define IDC_ABOUT 40001 92 | #define IDC_EXIT 40002 93 | #define IDC_SYMMETRY 40003 94 | #define IDC_TRANSLATE 40004 95 | #define IDC_PERIODROWSCOLS 40006 96 | #define IDC_SEARCHSETTINGS 40007 97 | #define IDC_OUTPUTSETTINGS 40009 98 | #define IDC_PREFERENCES 40010 99 | #define IDC_SELECTMODE 40011 100 | #define IDC_OPENSTATE 40013 101 | #define IDC_SAVEGAME 40014 102 | #define IDC_SEARCHSTART 40015 103 | #define IDC_SEARCHPAUSE 40016 104 | #define IDC_SEARCHRESET 40017 105 | #define IDC_SEARCHRESUME 40018 106 | #define IDC_SEARCHBACKUP 40019 107 | #define IDC_CLEAR 40022 108 | #define IDC_CLEARGEN 40023 109 | #define IDC_NEXTGEN 40024 110 | #define IDC_PREVGEN 40025 111 | #define IDC_EDITCOPY 40026 112 | #define IDC_UPDATEDISPLAY 40027 113 | #define IDC_SEARCHBACKUP2 40028 114 | #define IDC_COPYRESULT 40029 115 | #define IDC_SHIFTGUP 40032 116 | #define IDC_SHIFTGDOWN 40033 117 | #define IDC_SHIFTGRIGHT 40035 118 | #define IDC_SHIFTGLEFT 40036 119 | #define IDC_SHIFTAUP 40037 120 | #define IDC_SHIFTADOWN 40038 121 | #define IDC_SHIFTALEFT 40039 122 | #define IDC_SHIFTARIGHT 40040 123 | #define IDC_SHIFTAPAST 40041 124 | #define IDC_SHIFTAFUTURE 40042 125 | #define IDC_SEARCHPREPARE 40043 126 | #define IDC_COPYCOMBINATION 40044 127 | #define IDC_CLEARCOMBINATION 40047 128 | #define ID_TRANS_GEN 40055 129 | #define ID_TRANS_ALL 40056 130 | #define ID_FLIP_GEN_H 40057 131 | #define ID_FLIP_GEN_V 40058 132 | #define ID_FLIP_ALL_H 40059 133 | #define ID_FLIP_ALL_V 40060 134 | #define ID_HIDESEL 40061 135 | 136 | // Next default values for new objects 137 | // 138 | #ifdef APSTUDIO_INVOKED 139 | #ifndef APSTUDIO_READONLY_SYMBOLS 140 | #define _APS_NEXT_RESOURCE_VALUE 136 141 | #define _APS_NEXT_COMMAND_VALUE 40062 142 | #define _APS_NEXT_CONTROL_VALUE 1100 143 | #define _APS_NEXT_SYMED_VALUE 101 144 | #endif 145 | #endif 146 | -------------------------------------------------------------------------------- /source/lifesrc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Life search program include file. 3 | * Original author: David I. Bell. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define WLS_VERSION_STRING _T("0.71") 12 | 13 | #define ROWMAX 250 /* maximum rows for search rectangle */ 14 | #define COLMAX 250 /* maximum columns for search rectangle */ 15 | #define GENMAX 20 /* maximum number of generations */ 16 | #define TRANSMAX 10 /* largest translation value allowed */ 17 | 18 | 19 | /* 20 | * Build options 21 | */ 22 | #ifndef DEBUGFLAG 23 | #define DEBUGFLAG 0 /* nonzero for debugging features */ 24 | #endif 25 | 26 | 27 | /* 28 | * Other definitions 29 | */ 30 | #ifdef JS 31 | #define DUMPVERSION 56 /* version of dump file (was 6) */ 32 | #else 33 | #define DUMPVERSION 102 /* version of dump file */ 34 | #endif 35 | 36 | #define ALLOCSIZE 2048 /* chunk size for cell allocation */ 37 | #define LINESIZE 1000 /* size of input lines */ 38 | 39 | 40 | /* 41 | * Debugging macros 42 | */ 43 | #if DEBUGFLAG 44 | #define DPRINTF0(fmt) if (debug) printf(fmt) 45 | #define DPRINTF1(fmt,a1) if (debug) printf(fmt,a1) 46 | #define DPRINTF2(fmt,a1,a2) if (debug) printf(fmt,a1,a2) 47 | #define DPRINTF3(fmt,a1,a2,a3) if (debug) printf(fmt,a1,a2,a3) 48 | #define DPRINTF4(fmt,a1,a2,a3,a4) if (debug) printf(fmt,a1,a2,a3,a4) 49 | #define DPRINTF5(fmt,a1,a2,a3,a4,a5) if (debug) printf(fmt,a1,a2,a3,a4,a5) 50 | #else 51 | #define DPRINTF0(fmt) 52 | #define DPRINTF1(fmt,a1) 53 | #define DPRINTF2(fmt,a1,a2) 54 | #define DPRINTF3(fmt,a1,a2,a3) 55 | #define DPRINTF4(fmt,a1,a2,a3,a4) 56 | #define DPRINTF5(fmt,a1,a2,a3,a4,a5) 57 | #endif 58 | 59 | 60 | //#define isdigit(ch) (((ch) >= '0') && ((ch) <= '9')) 61 | #define isblank(ch) (((ch) == ' ') || ((ch) == '\t')) 62 | 63 | typedef unsigned char PACKED_BOOL; 64 | typedef unsigned char STATE; 65 | typedef unsigned int STATUS; 66 | 67 | /* 68 | * Status returned by routines 69 | */ 70 | #define OK ((STATUS) 0) 71 | #define ERROR1 ((STATUS) 1) 72 | #define CONSISTENT ((STATUS) 2) 73 | #define NOTEXIST ((STATUS) 3) 74 | #define FOUND ((STATUS) 4) 75 | 76 | 77 | /* 78 | * States of a cell 79 | */ 80 | #ifdef JS 81 | #define OFF ((STATE) 0x00) /* cell is known off */ 82 | #define ON ((STATE) 0x01) /* cell is known on */ 83 | #define UNK ((STATE) 0x10) /* cell is unknown */ 84 | #define NSTATES 3 /* number of states */ 85 | #else 86 | #define UNK ((STATE) 0) /* cell is unknown */ 87 | #define ON ((STATE) 1) /* cell is known on */ 88 | #define OFF ((STATE) 9) /* cell is known off */ 89 | #define NSTATES 3 /* number of states */ 90 | #endif 91 | 92 | /* 93 | * Information about a row. 94 | */ 95 | typedef struct { 96 | int oncount; /* number of cells which are set on */ 97 | } ROWINFO; 98 | 99 | 100 | /* 101 | * Information about a column. 102 | */ 103 | typedef struct { 104 | int setcount; /* number of cells which are set */ 105 | int oncount; /* number of cells which are set on */ 106 | int sumpos; /* sum of row positions for on cells */ 107 | } COLINFO; 108 | 109 | 110 | /* 111 | * Information about one cell of the search. 112 | */ 113 | typedef struct cell CELL; 114 | 115 | struct cell { 116 | // state is the most used field so let's put it first 117 | 118 | STATE state; /* current state */ 119 | 120 | // it makes one byte 121 | // let's align the address before the pointers start 122 | 123 | PACKED_BOOL free; /* TRUE if this cell still has free choice */ 124 | 125 | // aligned to two bytes - let's round it up to four 126 | 127 | short gen; /* generation number of this cell */ 128 | short row; /* row of this cell */ 129 | short col; /* column of this cell */ 130 | 131 | // and now for the pointers 132 | 133 | CELL * past; /* cell in the past at this location */ 134 | CELL * future; /* cell in the future at this location */ 135 | CELL * cul; /* cell to up and left */ 136 | CELL * cu; /* cell to up */ 137 | CELL * cur; /* cell to up and right */ 138 | CELL * cl; /* cell to left */ 139 | CELL * cr; /* cell to right */ 140 | CELL * cdl; /* cell to down and left */ 141 | CELL * cd; /* cell to down */ 142 | CELL * cdr; /* cell to down and right */ 143 | CELL * loop; /* next cell in same loop as this one */ 144 | CELL * search; /* cell next to be searched for setting */ 145 | 146 | ROWINFO * rowinfo; /* information about this cell's row */ 147 | COLINFO * colinfo; /* information about this cell's column */ 148 | 149 | short near1; /* count of cells this cell is near */ 150 | 151 | PACKED_BOOL frozen; /* TRUE if this cell is frozen in all gens */ 152 | #ifdef JS 153 | PACKED_BOOL choose; /* TRUE if can choose this cell if unknown */ 154 | #else 155 | PACKED_BOOL active; /* FALSE if mirror by a symmetry */ 156 | PACKED_BOOL unchecked; /* TRUE for unchecked cells */ 157 | 158 | STATE combined; 159 | #endif 160 | }; 161 | 162 | 163 | typedef unsigned char FLAGS; 164 | 165 | typedef unsigned char WLS_CELLVAL; 166 | 167 | struct field_struct { 168 | // Except for brief periods when allocating new fields, it's assumed that 169 | // ngens==g.period, nrows==g.nrows, ncols==g.ncols. 170 | int ngens; 171 | int nrows; 172 | int ncols; 173 | int gen_stride; 174 | int row_stride; 175 | 176 | #define CV_FORCEDOFF 0 // cell values - These must not be changed 177 | #define CV_FORCEDON 1 178 | #define CV_CLEAR 2 179 | #define CV_UNCHECKED 3 180 | #define CV_FROZEN 4 181 | #define CV_INVALID 255 182 | WLS_CELLVAL *c; 183 | }; 184 | 185 | // These may be implemented as functions or as macros. 186 | // It's not okay to use wlsCellVal() as an lvalue. 187 | #define wlsCellVal(f,k,i,j) ((f)->c[(k)*(f)->gen_stride + (j)*(f)->row_stride + (i)]) 188 | #define wlsSetCellVal(f,k,i,j,v) ((f)->c[(k)*(f)->gen_stride + (j)*(f)->row_stride + (i)])=(v) 189 | 190 | void wlsSetCellVal_Safe(struct field_struct *field, int k, int i, int j, WLS_CELLVAL v); 191 | 192 | struct globals_struct { 193 | /* 194 | * Current parameter values for the program to be saved over runs. 195 | * These values are dumped and loaded by the dump and load commands. 196 | * If you add another parameter, be sure to also add it to param_table, 197 | * preferably at the end so as to minimize dump file incompatibilities. 198 | */ 199 | STATUS curstatus; /* current status of search */ 200 | int nrows; /* number of rows available to the current search */ 201 | int ncols; /* number of columns available to the current search */ 202 | int period; /* number of generations in the current search */ 203 | int rowtrans; /* translation of rows */ 204 | int coltrans; /* translation of columns */ 205 | BOOL rowsym; /* enable row symmetry starting at column */ 206 | BOOL colsym; /* enable column symmetry starting at row */ 207 | BOOL pointsym; /* enable symmetry with central point */ 208 | BOOL fwdsym; /* enable forward diagonal symmetry */ 209 | BOOL bwdsym; /* enable backward diagonal symmetry */ 210 | BOOL fliprows; /* flip rows at column number from last to first generation */ 211 | BOOL flipcols; /* flip columns at row number from last to first generation */ 212 | BOOL flipquads; /* flip quadrants from last to first gen */ 213 | BOOL parent; /* only look for parents */ 214 | BOOL allobjects; /* look for all objects including subperiods */ 215 | int nearcols; /* maximum distance to be near columns */ 216 | int maxcount; /* maximum number of cells in generation 0 */ 217 | int userow; /* row that must have at least one ON cell */ 218 | int usecol; /* column that must have at least one ON cell */ 219 | int colcells; /* maximum cells in a column */ 220 | int colwidth; /* maximum width of each column */ 221 | BOOL follow; /* follow average position of previous column */ 222 | BOOL orderwide; /* ordering tries to find wide objects */ 223 | BOOL ordergens; /* ordering tries all gens first */ 224 | BOOL ordermiddle; /* ordering tries middle columns first (deprecated) */ 225 | BOOL followgens; /* try to follow setting of other gens */ 226 | 227 | #ifndef JS 228 | BOOL smart; /* use smart method (KAS) */ 229 | BOOL smarton; 230 | BOOL combine; 231 | BOOL combining; 232 | int smartwindow; /* no. of cells to check */ 233 | int smartthreshold; /* check threshold */ 234 | int smartstatlen; 235 | int smartstatwnd; 236 | int smartstatsumlen; 237 | int smartstatsumwnd; 238 | int smartstatsumlenc; 239 | int smartstatsumwndc; 240 | #endif 241 | 242 | #define SORTORDER_DEFAULT 0 243 | #define SORTORDER_DIAG 1 244 | #define SORTORDER_KNIGHT 2 245 | #define SORTORDER_TOPDOWN 3 246 | #define SORTORDER_CENTEROUT 4 247 | #define SORTORDER_MIDDLECOLOUT 5 248 | int sortorder; 249 | 250 | int knightsort; // deprecated 251 | 252 | #ifdef JS 253 | int fastsym; 254 | #endif 255 | int symmetry; 256 | int trans_rotate; 257 | int trans_flip; 258 | int trans_x; 259 | int trans_y; 260 | 261 | /* 262 | * These values are not affected when dumping and loading since they 263 | * do not affect the status of a search in progress. 264 | * They are either settable on the command line or are computed. 265 | */ 266 | BOOL debug; /* enable debugging output (if compiled so) */ 267 | BOOL inited; /* initialization has been done */ 268 | #ifdef JS 269 | STATE bornrules[16]; /* rules for whether a cell is to be born */ 270 | STATE liverules[16]; /* rules for whether a live cell stays alive */ 271 | #else 272 | BOOL bornrules[16]; /* rules for whether a cell is to be born */ 273 | BOOL liverules[16]; /* rules for whether a live cell stays alive */ 274 | #endif 275 | int curgen; /* current generation for display */ 276 | int outputcols; /* number of columns to save for output */ 277 | int outputlastcols; /* last number of columns output */ 278 | #ifndef JS 279 | int g0oncellcount; /* number of live cells in generation 0 */ 280 | #endif 281 | int cellcount; /* number of live cells in generation 0 */ 282 | long dumpfreq; /* how often to perform dumps */ 283 | long dumpcount; /* counter for dumps */ 284 | int viewfreq; /* how often to view results */ 285 | long viewcount; /* counter for viewing */ 286 | TCHAR dumpfile[80]; /* dump file name */ 287 | TCHAR outputfile[80]; /* file to output results to */ 288 | #ifndef JS 289 | int smartlen0; 290 | int smartlen1; 291 | int smartcomb; 292 | STATE smartchoice; /* preferred state for the selected cell */ 293 | STATE prevstate; /* the state of the last free cell before backup() */ 294 | #endif 295 | 296 | /* 297 | * Data about all of the cells. 298 | */ 299 | CELL ** settable; /* table of (MAXCELLS) cells whose value is set */ 300 | CELL ** newset; /* where to add new cells into setting table */ 301 | CELL ** nextset; /* next cell in setting table to examine */ 302 | #ifdef JS 303 | CELL ** baseset; /* base of changeable part of setting table */ 304 | CELL * fullsearchlist; /* complete list of cells to search */ 305 | #else 306 | CELL ** searchtable; /* a stack of (MAXCELLS) searchlist positions */ 307 | CELL ** searchset; 308 | #endif 309 | ROWINFO rowinfo[ROWMAX]; /* information about rows of gen 0 */ 310 | COLINFO colinfo[COLMAX]; /* information about columns of gen 0 */ 311 | int fullcolumns; /* columns in gen 0 which are fully set */ 312 | #ifndef JS 313 | int combinedcells; 314 | int setcombinedcells; 315 | int differentcombinedcells; 316 | #endif 317 | 318 | // Represents the editable cells 319 | struct field_struct *field; 320 | // Used for the current state of the search, and other temporary things 321 | struct field_struct *tmpfield; 322 | 323 | #define WLS_RULESTRING_LEN 50 324 | TCHAR rulestring[WLS_RULESTRING_LEN]; 325 | int saveoutput; 326 | #ifndef JS 327 | int saveoutputallgen; 328 | int stoponfound; 329 | int stoponstep; 330 | #endif 331 | TCHAR state_filename[MAX_PATH]; 332 | int foundcount; 333 | int writecount; 334 | #ifdef JS 335 | BOOL islife; /* whether the rules are for standard Life */ 336 | #endif 337 | 338 | #ifdef JS 339 | /* 340 | * Table of transitions. 341 | * Given the state of a cell and its neighbors in one generation, 342 | * this table determines the state of the cell in the next generation. 343 | * The table is indexed by the descriptor value of a cell. 344 | */ 345 | STATE transit[256]; 346 | #endif 347 | 348 | /* 349 | * Table of implications. 350 | * Given the state of a cell and its neighbors in one generation, 351 | * this table determines deductions about the cell and its neighbors 352 | * in the previous generation. 353 | * The table is indexed by the descriptor value of a cell. 354 | */ 355 | #ifdef JS 356 | #define WLS_IMPLIC_LEN 256 357 | #else 358 | #define WLS_IMPLIC_LEN 1000 359 | #endif 360 | FLAGS implic[WLS_IMPLIC_LEN]; 361 | 362 | int newcellcount; /* number of cells ready for allocation */ 363 | int auxcellcount; /* number of cells in auxillary table */ 364 | CELL * newcells; /* cells ready for allocation */ 365 | CELL * searchlist; /* current list of cells to search */ 366 | ROWINFO dummyrowinfo; /* dummy info for ignored cells */ 367 | COLINFO dummycolinfo; /* dummy info for ignored cells */ 368 | #ifdef JS 369 | CELL * deadcell; /* boundary cell value */ 370 | #endif 371 | CELL ** celltable; /* table of (MAXCELLS) usual cells */ 372 | CELL ** auxtable; /* table of (auxtable_alloc) auxillary cells */ 373 | int auxtable_alloc; 374 | 375 | void *memblks[2000]; 376 | int memblks_used; 377 | 378 | int lifesrc_maxcells; // formerly the MAXCELLS macro 379 | }; 380 | 381 | 382 | /* 383 | * Global procedures 384 | */ 385 | 386 | BOOL initcells(void); 387 | 388 | #ifndef JS 389 | void initsearchorder(void); 390 | #endif 391 | 392 | void wlsUpdateAndShowTmpField(void); 393 | void wlsUpdateAndShowTmpField_Sync(void); 394 | void wlsWriteCurrentFieldToFile(HWND hwndParent, TCHAR *file1, BOOL append); 395 | void wlsWriteCurrentFieldToFile_internal(const TCHAR *filename, BOOL append); 396 | void dumpstate(HWND hwndParent, TCHAR *file1, BOOL echo); 397 | void dumpstate_internal(const TCHAR *filename, BOOL echo); 398 | 399 | void adjustnear(CELL *, int); 400 | STATUS search(void); 401 | BOOL proceed(CELL *, STATE, BOOL); 402 | BOOL go(CELL *, STATE, BOOL); 403 | BOOL setcell(CELL *, STATE, BOOL); 404 | #ifndef JS 405 | STATUS examinenext(void); 406 | #endif 407 | 408 | CELL * findcell(int, int, int); 409 | CELL * backup(void); 410 | BOOL subperiods(void); 411 | BOOL loopcells(CELL *, CELL *); 412 | #ifdef JS 413 | void excludecone(int, int, int); 414 | #endif 415 | BOOL freezecell(int, int); 416 | BOOL setrules(TCHAR *); 417 | BOOL setrulesA(char *); 418 | BOOL loadstate_from_filename(const TCHAR *filename); 419 | void getbackup(char *cp); 420 | 421 | struct wcontext; 422 | void wlsErrorf(struct wcontext *ctx, TCHAR *fmt, ...); 423 | void wlsMessagef(struct wcontext *ctx, TCHAR *fmt, ...); 424 | void wlsStatusf(struct wcontext *ctx, TCHAR *fmt, ...); 425 | void ttystatus(TCHAR *, ...); 426 | void record_malloc(int func,void *m); 427 | #ifndef JS 428 | BOOL set_initial_cells(void); 429 | #endif 430 | void wlsUpdateProgressCounter(void); 431 | void wlsNewField(void); 432 | -------------------------------------------------------------------------------- /scripts/wls2008.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 18 | 19 | 20 | 21 | 22 | 29 | 32 | 35 | 38 | 41 | 44 | 59 | 62 | 65 | 68 | 76 | 79 | 82 | 85 | 88 | 91 | 94 | 97 | 98 | 105 | 108 | 111 | 114 | 117 | 121 | 136 | 139 | 142 | 145 | 153 | 156 | 159 | 162 | 165 | 168 | 171 | 174 | 175 | 183 | 186 | 189 | 192 | 195 | 198 | 215 | 218 | 221 | 224 | 234 | 237 | 240 | 243 | 246 | 249 | 252 | 255 | 256 | 264 | 267 | 270 | 273 | 276 | 280 | 297 | 300 | 303 | 306 | 316 | 319 | 322 | 325 | 328 | 331 | 334 | 337 | 338 | 345 | 348 | 351 | 354 | 357 | 360 | 375 | 378 | 381 | 384 | 391 | 394 | 397 | 400 | 403 | 406 | 409 | 412 | 413 | 420 | 423 | 426 | 429 | 432 | 436 | 451 | 454 | 457 | 460 | 467 | 470 | 473 | 476 | 479 | 482 | 485 | 488 | 489 | 497 | 500 | 503 | 506 | 509 | 512 | 529 | 532 | 535 | 538 | 547 | 550 | 553 | 556 | 559 | 562 | 565 | 568 | 569 | 577 | 580 | 583 | 586 | 589 | 593 | 610 | 613 | 616 | 619 | 628 | 631 | 634 | 637 | 640 | 643 | 646 | 649 | 650 | 651 | 652 | 653 | 654 | 659 | 662 | 663 | 666 | 667 | 670 | 671 | 674 | 677 | 681 | 682 | 685 | 689 | 690 | 693 | 697 | 698 | 701 | 705 | 706 | 707 | 708 | 713 | 716 | 717 | 720 | 721 | 724 | 725 | 726 | 731 | 734 | 735 | 738 | 739 | 742 | 743 | 746 | 747 | 750 | 751 | 754 | 755 | 758 | 759 | 762 | 763 | 766 | 767 | 770 | 771 | 774 | 775 | 778 | 779 | 782 | 783 | 786 | 787 | 790 | 791 | 794 | 795 | 798 | 799 | 802 | 803 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | -------------------------------------------------------------------------------- /source/wls.rc: -------------------------------------------------------------------------------- 1 | //Microsoft Developer Studio generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winresrc.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (U.S.) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | #ifdef _WIN32 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | #pragma code_page(1252) 22 | #endif //_WIN32 23 | 24 | #ifdef JS 25 | 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | // 29 | // Dialog 30 | // 31 | 32 | DLGSETTINGS DIALOGEX 0, 0, 310, 114 33 | STYLE DS_SETFONT | DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 34 | CAPTION "Output settings" 35 | FONT 8, "MS Shell Dlg" 36 | BEGIN 37 | LTEXT "Dump to file:",IDC_STATIC,7,10,47,8 38 | EDITTEXT IDC_DUMPFILE,54,7,51,14,ES_AUTOHSCROLL | WS_GROUP 39 | LTEXT "every",IDC_STATIC,113,10,18,8 40 | EDITTEXT IDC_DUMPFREQ,137,7,40,14,ES_AUTOHSCROLL | WS_GROUP 41 | LTEXT "calculations",IDC_STATIC,183,10,38,8 42 | CONTROL "Stop when an object is found",IDC_STOPONFOUND,"Button", 43 | BS_AUTOCHECKBOX | WS_TABSTOP | WS_GROUP,7,27,108,10 44 | CONTROL "Save found objects to file:",IDC_OUTPUTFILEYN,"Button", 45 | BS_AUTOCHECKBOX | WS_TABSTOP,7,42,98,10 46 | EDITTEXT IDC_OUTPUTFILE,105,40,58,14,ES_AUTOHSCROLL | WS_GROUP 47 | LTEXT "Save partial results every",IDC_STATIC,7,60,97,8 48 | EDITTEXT IDC_OUTPUTCOLS,104,58,40,14,ES_AUTOHSCROLL | WS_GROUP 49 | LTEXT "columns [incompatible with Fast symmetry]",IDC_STATIC, 50 | 147,60,143,8 51 | DEFPUSHBUTTON "OK",IDOK,252,7,50,14 | WS_GROUP 52 | PUSHBUTTON "Cancel",IDCANCEL,252,24,50,14 53 | END 54 | 55 | VS_VERSION_INFO VERSIONINFO 56 | FILEVERSION 0,7,1,0 57 | PRODUCTVERSION 0,7,1,0 58 | FILEOS VOS_NT_WINDOWS32 59 | FILETYPE VFT_APP 60 | FILESUBTYPE VFT2_UNKNOWN 61 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 62 | #ifdef _DEBUG 63 | FILEFLAGS VS_FF_DEBUG 64 | #else 65 | FILEFLAGS 0x0L 66 | #endif 67 | { 68 | BLOCK "StringFileInfo" 69 | { 70 | BLOCK "040904E4" 71 | { 72 | VALUE "Comments", "" 73 | VALUE "CompanyName", "" 74 | VALUE "FileDescription", "WinLifeSearchJ" 75 | VALUE "FileVersion", "0, 7, 1, 0" 76 | VALUE "InternalName", "wlsj" 77 | VALUE "LegalCopyright", "(C) 2012 Jason Summers et. al." 78 | VALUE "LegalTrademarks", "" 79 | VALUE "OriginalFilename", "wlsj.exe" 80 | VALUE "PrivateBuild", "" 81 | VALUE "ProductName", "WinLifeSearchJ" 82 | VALUE "ProductVersion", "0, 7, 1, 0" 83 | VALUE "SpecialBuild", "" 84 | } 85 | } 86 | BLOCK "VarFileInfo" 87 | { 88 | VALUE "Translation", 0x0409, 0x04E4 89 | } 90 | } 91 | 92 | 93 | #else // KS: 94 | 95 | ///////////////////////////////////////////////////////////////////////////// 96 | // 97 | // Dialog 98 | // 99 | 100 | DLGSETTINGS DIALOGEX 0, 0, 301, 110 101 | STYLE DS_SETFONT | DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 102 | CAPTION "Output settings" 103 | FONT 8, "MS Shell Dlg" 104 | BEGIN 105 | CONTROL "Stop after each calculation",IDC_STOPONSTEP,"Button", 106 | BS_AUTOCHECKBOX | WS_TABSTOP | WS_GROUP,7,10,101,10 107 | CONTROL "Stop when an object is found",IDC_STOPONFOUND,"Button", 108 | BS_AUTOCHECKBOX | WS_TABSTOP,7,26,108,10 109 | CONTROL "Save found objects to file:",IDC_OUTPUTFILEYN,"Button", 110 | BS_AUTOCHECKBOX | WS_TABSTOP,7,42,98,10 111 | EDITTEXT IDC_OUTPUTFILE,106,40,56,14,ES_AUTOHSCROLL | WS_GROUP 112 | CONTROL "All generations",IDC_OUTPUTALLGEN,"Button", 113 | BS_AUTOCHECKBOX | WS_TABSTOP | WS_GROUP,170,42,62,10 114 | 115 | LTEXT "Dump to file:",IDC_STATIC,7,59,40,8 116 | EDITTEXT IDC_DUMPFILE,106,56,56,14,ES_AUTOHSCROLL | WS_GROUP 117 | LTEXT "every",IDC_STATIC,170,59,18,8 118 | EDITTEXT IDC_DUMPFREQ,193,56,56,14,ES_AUTOHSCROLL | WS_GROUP 119 | LTEXT "calculations",IDC_STATIC,255,59,38,8 120 | 121 | LTEXT "Save partial results every",IDC_STATIC,7,75,80,8 122 | EDITTEXT IDC_OUTPUTCOLS,106,72,56,14,ES_AUTOHSCROLL | WS_GROUP 123 | LTEXT "columns",IDC_STATIC,170,75,27,8 124 | 125 | DEFPUSHBUTTON "OK",IDOK,243,8,50,14 | WS_GROUP 126 | PUSHBUTTON "Cancel",IDCANCEL,243,24,50,14 127 | END 128 | 129 | VS_VERSION_INFO VERSIONINFO 130 | FILEVERSION 0,7,1,0 131 | PRODUCTVERSION 0,7,1,0 132 | FILEOS VOS_NT_WINDOWS32 133 | FILETYPE VFT_APP 134 | FILESUBTYPE VFT2_UNKNOWN 135 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 136 | #ifdef _DEBUG 137 | FILEFLAGS VS_FF_DEBUG 138 | #else 139 | FILEFLAGS 0x0L 140 | #endif 141 | { 142 | BLOCK "StringFileInfo" 143 | { 144 | BLOCK "040904E4" 145 | { 146 | VALUE "Comments", "" 147 | VALUE "CompanyName", "" 148 | VALUE "FileDescription", "WinLifeSearchK" 149 | VALUE "FileVersion", "0, 7, 1, 0" 150 | VALUE "InternalName", "wls" 151 | VALUE "LegalCopyright", "(C) 2012 Jason Summers et. al." 152 | VALUE "LegalTrademarks", "" 153 | VALUE "OriginalFilename", "wls.exe" 154 | VALUE "PrivateBuild", "" 155 | VALUE "ProductName", "WinLifeSearchK" 156 | VALUE "ProductVersion", "0, 7, 1, 0" 157 | VALUE "SpecialBuild", "" 158 | } 159 | } 160 | BLOCK "VarFileInfo" 161 | { 162 | VALUE "Translation", 0x0409, 0x04E4 163 | } 164 | } 165 | 166 | 167 | #endif // JS/KS 168 | 169 | DLGSYMMETRY DIALOGEX 0, 0, 310, 178 170 | STYLE DS_SETFONT | DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 171 | CAPTION "Symmetry" 172 | FONT 8, "MS Shell Dlg" 173 | BEGIN 174 | CONTROL IDB_SYM0,IDC_STATIC,"Static",SS_BITMAP,7,7,33,30 175 | CONTROL "&None",IDC_SYM0,"Button",BS_AUTORADIOBUTTON | WS_GROUP, 176 | 45,18,33,10 177 | CONTROL "Mirror-&X",IDC_SYM1,"Button",BS_AUTORADIOBUTTON,45,50, 178 | 40,10 179 | CONTROL "Mirror-&Y",IDC_SYM2,"Button",BS_AUTORADIOBUTTON,45,83, 180 | 40,10 181 | CONTROL "Diagonal-&Forward",IDC_SYM3,"Button",BS_AUTORADIOBUTTON, 182 | 45,116,71,10 183 | CONTROL "Diagonal-&Backward",IDC_SYM4,"Button", 184 | BS_AUTORADIOBUTTON,45,149,78,10 185 | CONTROL "&Origin",IDC_SYM5,"Button",BS_AUTORADIOBUTTON,178,17,34, 186 | 10 187 | CONTROL "&Mirror-Both",IDC_SYM6,"Button",BS_AUTORADIOBUTTON,178, 188 | 50,50,10 189 | CONTROL "&Diagonal-Both",IDC_SYM7,"Button",BS_AUTORADIOBUTTON, 190 | 178,83,61,10 191 | CONTROL "90 degree &rotational",IDC_SYM8,"Button", 192 | BS_AUTORADIOBUTTON,178,116,79,10 193 | CONTROL "8-fo&ld",IDC_SYM9,"Button",BS_AUTORADIOBUTTON,178,149, 194 | 33,10 195 | CONTROL IDB_SYM1,IDC_STATIC,"Static",SS_BITMAP,7,40,33,30 196 | CONTROL IDB_SYM2,IDC_STATIC,"Static",SS_BITMAP,7,73,33,30 197 | CONTROL IDB_SYM3,IDC_STATIC,"Static",SS_BITMAP,7,106,33,30 198 | CONTROL IDB_SYM4,IDC_STATIC,"Static",SS_BITMAP,7,139,33,30 199 | CONTROL IDB_SYM5,IDC_STATIC,"Static",SS_BITMAP,140,7,33,30 200 | CONTROL IDB_SYM6,IDC_STATIC,"Static",SS_BITMAP,140,40,33,30 201 | CONTROL IDB_SYM7,IDC_STATIC,"Static",SS_BITMAP,140,73,33,30 202 | CONTROL IDB_SYM8,IDC_STATIC,"Static",SS_BITMAP,140,106,33,30 203 | CONTROL IDB_SYM9,IDC_STATIC,"Static",SS_BITMAP,140,139,33,30 204 | DEFPUSHBUTTON "OK",IDOK,253,7,50,14,WS_GROUP 205 | PUSHBUTTON "Cancel",IDCANCEL,253,24,50,14 206 | END 207 | 208 | DLGABOUT DIALOGEX 0, 0, 245, 71 209 | STYLE DS_SETFONT | DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 210 | CAPTION "About" 211 | FONT 8, "MS Shell Dlg" 212 | BEGIN 213 | DEFPUSHBUTTON "OK",IDOK,188,7,50,14 214 | LTEXT "",IDC_ABOUTTEXT,7,7,171,57 215 | ICON "ICONWLS",IDC_STATIC,201,34,21,20,SS_REALSIZEIMAGE 216 | END 217 | 218 | DLGPERIODROWSCOLS DIALOGEX 0, 0, 216, 72 219 | STYLE DS_SETFONT | DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 220 | CAPTION "Period, rows, columns" 221 | FONT 8, "MS Shell Dlg" 222 | BEGIN 223 | LTEXT "Static",IDC_PERIODTEXT,7,9,78,15,WS_GROUP 224 | EDITTEXT IDC_PERIOD,87,7,40,14,ES_AUTOHSCROLL | ES_NUMBER 225 | LTEXT "&Columns (X)",IDC_STATIC,7,26,78,8,WS_GROUP 226 | EDITTEXT IDC_COLUMNS,87,24,40,14,ES_AUTOHSCROLL | ES_NUMBER 227 | LTEXT "&Rows (Y)",IDC_STATIC,7,43,78,8,WS_GROUP 228 | EDITTEXT IDC_ROWS,87,41,40,14,ES_AUTOHSCROLL | ES_NUMBER 229 | DEFPUSHBUTTON "OK",IDOK,159,7,50,14,WS_GROUP 230 | PUSHBUTTON "Cancel",IDCANCEL,159,24,50,14 231 | END 232 | 233 | DLGTRANSLATE DIALOGEX 0, 0, 253, 190 234 | STYLE DS_SETFONT | DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 235 | CAPTION "Translation" 236 | FONT 8, "MS Shell Dlg" 237 | BEGIN 238 | LTEXT "X",IDC_STATIC,7,10,8,8,WS_GROUP 239 | EDITTEXT IDC_TRANSX,25,7,40,14,ES_AUTOHSCROLL 240 | LTEXT "cells right",IDC_STATIC,69,10,30,8 241 | LTEXT "Y",IDC_STATIC,7,29,8,8 242 | EDITTEXT IDC_TRANSY,25,26,40,14,ES_AUTOHSCROLL 243 | LTEXT "cells down",IDC_STATIC,69,28,34,8 244 | CONTROL "None",IDC_TRANS0,"Button",BS_AUTORADIOBUTTON | WS_GROUP | 245 | WS_TABSTOP,19,67,33,10 246 | CONTROL "Rotate 90",IDC_TRANS1,"Button",BS_AUTORADIOBUTTON,19,81, 247 | 47,10 248 | CONTROL "Rotate 180",IDC_TRANS2,"Button",BS_AUTORADIOBUTTON,19, 249 | 95,51,10 250 | CONTROL "Rotate 270",IDC_TRANS3,"Button",BS_AUTORADIOBUTTON,19, 251 | 109,51,10 252 | CONTROL "Flip horizontal",IDC_TRANS4,"Button",BS_AUTORADIOBUTTON, 253 | 19,123,59,10 254 | CONTROL "Flip bkwd diag",IDC_TRANS5,"Button",BS_AUTORADIOBUTTON, 255 | 19,137,61,10 256 | CONTROL "Flip vertical",IDC_TRANS6,"Button",BS_AUTORADIOBUTTON, 257 | 19,151,51,10 258 | CONTROL "Flip fwd diag",IDC_TRANS7,"Button",BS_AUTORADIOBUTTON, 259 | 19,165,55,10 260 | CONTROL IDB_TRANS0,IDC_STATIC,"Static",SS_BITMAP,7,67,9,9 261 | CONTROL IDB_TRANS1,IDC_STATIC,"Static",SS_BITMAP,7,81,9,9 262 | CONTROL IDB_TRANS2,IDC_STATIC,"Static",SS_BITMAP,7,95,9,9 263 | CONTROL IDB_TRANS3,IDC_STATIC,"Static",SS_BITMAP,7,110,9,9 264 | CONTROL IDB_TRANS4,IDC_STATIC,"Static",SS_BITMAP,7,123,9,9 265 | CONTROL IDB_TRANS5,IDC_STATIC,"Static",SS_BITMAP,7,137,9,9 266 | CONTROL IDB_TRANS6,IDC_STATIC,"Static",SS_BITMAP,7,151,9,9 267 | CONTROL IDB_TRANS7,IDC_STATIC,"Static",SS_BITMAP,7,166,9,9 268 | DEFPUSHBUTTON "OK",IDOK,196,7,50,14,WS_TABSTOP | WS_GROUP 269 | PUSHBUTTON "Cancel",IDCANCEL,196,24,50,14 270 | LTEXT "Any",IDC_STATIC,95,67,13,8 271 | CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,87,59,159,124 272 | LTEXT "Oscillators",IDC_STATIC,95,48,33,8 273 | LTEXT "Spaceships",IDC_STATIC,160,48,38,8 274 | LTEXT "4,8,12...",IDC_STATIC,95,81,27,8 275 | LTEXT "2,4,6...",IDC_STATIC,95,95,23,8 276 | LTEXT "4,8,12...",IDC_STATIC,95,109,27,8 277 | LTEXT "2,4,6... (orthogonal)",IDC_STATIC,160,123,62,8 278 | LTEXT "2,4,6...",IDC_STATIC,95,123,23,8 279 | LTEXT "2,4,6...",IDC_STATIC,95,137,23,8 280 | LTEXT "2,4,6...",IDC_STATIC,95,151,23,8 281 | LTEXT "2,4,6...",IDC_STATIC,95,165,23,8 282 | LTEXT "Any",IDC_STATIC,160,67,13,8 283 | LTEXT "None",IDC_STATIC,160,81,18,8 284 | LTEXT "None",IDC_STATIC,160,95,18,8 285 | LTEXT "None",IDC_STATIC,160,109,18,8 286 | LTEXT "4,6,8... (diagonal)",IDC_STATIC,160,137,56,8 287 | LTEXT "2,4,6... (orthogonal)",IDC_STATIC,160,151,62,8 288 | LTEXT "4,6,8... (diagonal)",IDC_STATIC,160,165,56,8 289 | END 290 | 291 | DLGSEARCHSETTINGS DIALOGEX 0, 0, 365, 222 292 | STYLE DS_SETFONT | DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 293 | CAPTION "Search settings" 294 | FONT 8, "MS Shell Dlg" 295 | BEGIN 296 | RTEXT "Lif&e rules:",IDC_STATIC,7,9,108,8 297 | EDITTEXT IDC_RULESTRING,120,7,60,14,ES_AUTOHSCROLL | WS_GROUP 298 | PUSHBUTTON "S&tandard life",IDC_CONWAY,185,7,50,14,WS_GROUP 299 | LTEXT "Force an ON cell in &column",IDC_STATIC,7,27,108,8,0, 300 | WS_EX_RIGHT 301 | EDITTEXT IDC_USECOL,120,24,60,14,ES_AUTOHSCROLL | WS_GROUP 302 | LTEXT "Force an ON cell in &row",IDC_STATIC,7,43,108,8,0, 303 | WS_EX_RIGHT 304 | EDITTEXT IDC_USEROW,120,40,60,14,ES_AUTOHSCROLL | WS_GROUP 305 | LTEXT "&Limit ON cells in generation 0 to:",IDC_STATIC,7,59, 306 | 108,8,0,WS_EX_RIGHT 307 | EDITTEXT IDC_MAXCOUNT,120,56,60,14,ES_AUTOHSCROLL | WS_GROUP 308 | LTEXT "Limit ON cells in each col&umn to:",IDC_STATIC,7,75,108, 309 | 8,0,WS_EX_RIGHT 310 | EDITTEXT IDC_COLCELLS,120,72,60,14,ES_AUTOHSCROLL | WS_GROUP 311 | LTEXT "Limit &width of each column to:",IDC_STATIC,7,91,108,8, 312 | 0,WS_EX_RIGHT 313 | EDITTEXT IDC_COLWIDTH,120,88,60,14,ES_AUTOHSCROLL | WS_GROUP 314 | LTEXT "ON cells must be with&in\r\nof ON cells in previous columns", 315 | IDC_STATIC,7,102,108,19,0,WS_EX_RIGHT 316 | EDITTEXT IDC_NEARCOLS,120,104,60,14,ES_AUTOHSCROLL | WS_GROUP 317 | CONTROL "Also find &subperiods",IDC_ALLOBJECTS,"Button", 318 | BS_AUTOCHECKBOX | WS_TABSTOP | WS_GROUP,7,138,79,10 319 | CONTROL "Find &parents only",IDC_PARENT,"Button",BS_AUTOCHECKBOX | 320 | WS_TABSTOP,7,154,69,10 321 | CONTROL "Combine solutions only",IDC_COMBINE,"Button", 322 | BS_AUTOCHECKBOX | WS_TABSTOP,7,170,87,10 323 | LTEXT "Sort order:",IDC_STATIC,185,44,40,10,WS_GROUP 324 | COMBOBOX IDC_SORTORDER,225,42,110,200,CBS_DROPDOWNLIST | 325 | WS_VSCROLL | WS_TABSTOP 326 | CONTROL "Sort to try all &gens first",IDC_ORDERGENS,"Button", 327 | BS_AUTOCHECKBOX | WS_TABSTOP | WS_GROUP,185,60,85,10 328 | CONTROL "S&ort to find wide objects",IDC_ORDERWIDE,"Button", 329 | BS_AUTOCHECKBOX | WS_TABSTOP,185,76,91,10 330 | CONTROL "Fast symmetry",IDC_FASTSYM,"Button",BS_AUTOCHECKBOX | 331 | WS_TABSTOP,185,92,60,10 332 | CONTROL "First try cells &near ON cells in the previous column", 333 | IDC_FOLLOW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,185,122, 334 | 173,10 335 | CONTROL "Try ON cells first if ON in an &adjacent generation", 336 | IDC_FOLLOWGENS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,185, 337 | 138,167,10 338 | CONTROL "First try cells with the greatest impact",IDC_SMART, 339 | "Button",BS_AUTOCHECKBOX | WS_TABSTOP,185,154,131,10 340 | CONTROL "Try ON cells first if they have greater impact", 341 | IDC_SMARTON,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,199, 342 | 170,153,10 343 | LTEXT "Impact checking window",IDC_ICWTEXT,185,187,108,8,0, 344 | WS_EX_RIGHT 345 | EDITTEXT IDC_SMARTWINDOW,297,184,28,14,ES_AUTOHSCROLL | WS_GROUP 346 | EDITTEXT IDC_SMARTWINDOWSTAT,330,184,28,14,ES_AUTOHSCROLL | 347 | ES_READONLY 348 | LTEXT "Impact checking threshold",IDC_ICTTEXT,185,203,108,8,0, 349 | WS_EX_RIGHT 350 | EDITTEXT IDC_SMARTTHRESHOLD,297,201,28,14,ES_AUTOHSCROLL | WS_GROUP 351 | EDITTEXT IDC_SMARTTHRESHOLDSTAT,330,201,28,14,ES_AUTOHSCROLL | 352 | ES_READONLY 353 | DEFPUSHBUTTON "OK",IDOK,308,7,50,14,WS_GROUP 354 | PUSHBUTTON "Cancel",IDCANCEL,308,24,50,14 355 | END 356 | 357 | DLGPREFERENCES DIALOGEX 0, 0, 308, 181 358 | STYLE DS_SETFONT | DS_FIXEDSYS | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 359 | CAPTION "Preferences" 360 | FONT 8, "MS Shell Dlg" 361 | BEGIN 362 | LTEXT "Update screen every",IDC_STATIC,7,9,80,8 363 | EDITTEXT IDC_VIEWFREQ,91,7,50,14,ES_AUTOHSCROLL | ES_NUMBER | WS_GROUP 364 | LTEXT "calculations",IDC_STATIC,146,9,50,8 365 | LTEXT "Cell size",IDC_STATIC,7,26,80,8 366 | EDITTEXT IDC_CELLSIZE,91,24,40,14,ES_AUTOHSCROLL | ES_NUMBER | WS_GROUP 367 | LTEXT "pixels",IDC_STATIC,136,26,50,8 368 | LTEXT "Default period",IDC_STATIC,7,43,80,15,WS_GROUP 369 | EDITTEXT IDC_DEFAULTPERIOD,91,41,40,14,ES_AUTOHSCROLL | ES_NUMBER 370 | LTEXT "Default columns (X)",IDC_STATIC,7,60,80,8,WS_GROUP 371 | EDITTEXT IDC_DEFAULTCOLUMNS,91,58,40,14,ES_AUTOHSCROLL | ES_NUMBER 372 | LTEXT "Default rows (Y)",IDC_STATIC,7,77,80,8,WS_GROUP 373 | EDITTEXT IDC_DEFAULTROWS,91,75,40,14,ES_AUTOHSCROLL | ES_NUMBER 374 | LTEXT "Search CPU priority",IDC_STATIC,7,94,80,8,WS_GROUP 375 | COMBOBOX IDC_SEARCHPRIORITY,91,92,80,200,CBS_DROPDOWNLIST | 376 | WS_VSCROLL | WS_TABSTOP 377 | LTEXT "Mouse wheel up =",IDC_STATIC,7,111,80,8,WS_GROUP 378 | COMBOBOX IDC_WHEELGENDIR,91,109,80,200,CBS_DROPDOWNLIST | 379 | WS_VSCROLL | WS_TABSTOP 380 | LTEXT "Save state folder",IDC_STATIC,7,128,80,8,WS_GROUP 381 | EDITTEXT IDC_SAVESTATEFOLDER,91,126,160,14,ES_AUTOHSCROLL 382 | PUSHBUTTON "Browse...",IDC_SAVESTATEBROWSE,251,126,40,14,WS_GROUP 383 | DEFPUSHBUTTON "OK",IDOK,251,7,50,14,WS_GROUP 384 | PUSHBUTTON "Cancel",IDCANCEL,251,24,50,14 385 | END 386 | 387 | ///////////////////////////////////////////////////////////////////////////// 388 | // 389 | // Menu 390 | // 391 | 392 | WLSMENU MENU DISCARDABLE 393 | BEGIN 394 | POPUP "&File" 395 | BEGIN 396 | MENUITEM "&Open state...\tCtrl+O", IDC_OPENSTATE 397 | MENUITEM "&Save state...\tCtrl+S", IDC_SAVEGAME 398 | MENUITEM SEPARATOR 399 | MENUITEM "E&xit", IDC_EXIT 400 | END 401 | POPUP "&Edit" 402 | BEGIN 403 | MENUITEM "&Copy\tCtrl+C", IDC_EDITCOPY 404 | MENUITEM SEPARATOR 405 | POPUP "&Shift" 406 | BEGIN 407 | POPUP "&Generation" 408 | BEGIN 409 | MENUITEM "&Up\tShift+Up", IDC_SHIFTGUP 410 | MENUITEM "&Down\tShift+Down", IDC_SHIFTGDOWN 411 | MENUITEM "&Left\tShift+Left", IDC_SHIFTGLEFT 412 | MENUITEM "&Right\tShift+Right", IDC_SHIFTGRIGHT 413 | END 414 | POPUP "&All generations" 415 | BEGIN 416 | MENUITEM "&Up\tCtrl+Shift+Up", IDC_SHIFTAUP 417 | MENUITEM "&Down\tCtrl+Shift+Down", IDC_SHIFTADOWN 418 | MENUITEM "&Left\tCtrl+Shift+Left", IDC_SHIFTALEFT 419 | MENUITEM "&Right\tCtrl+Shift+Right", IDC_SHIFTARIGHT 420 | MENUITEM "To &past\tCtrl+Shift+PgUp", IDC_SHIFTAPAST 421 | MENUITEM "To &future\tCtrl+Shift+PgDn", IDC_SHIFTAFUTURE 422 | END 423 | END 424 | POPUP "&Flip" 425 | BEGIN 426 | POPUP "&Generation" 427 | BEGIN 428 | MENUITEM "&Horizontal\tCtrl+H", ID_FLIP_GEN_H 429 | MENUITEM "&Vertical\tCtrl+E", ID_FLIP_GEN_V 430 | END 431 | POPUP "&All generations" 432 | BEGIN 433 | MENUITEM "&Horizontal\tCtrl+Shift+H", ID_FLIP_ALL_H 434 | MENUITEM "&Vertical\tCtrl+Shift+E", ID_FLIP_ALL_V 435 | END 436 | END 437 | POPUP "&Transpose" 438 | BEGIN 439 | MENUITEM "&Generation\tCtrl+T", ID_TRANS_GEN 440 | MENUITEM "&All generations\tCtrl+Shift+T", ID_TRANS_ALL 441 | END 442 | POPUP "Clea&r" 443 | BEGIN 444 | MENUITEM "&Generation\tCtrl+Del", IDC_CLEARGEN 445 | MENUITEM "&All generations\tCtrl+Shift+Del", IDC_CLEAR 446 | END 447 | MENUITEM SEPARATOR 448 | MENUITEM "&Hide selection\tBackspace", ID_HIDESEL 449 | END 450 | POPUP "View" 451 | BEGIN 452 | MENUITEM "&Next generation\tPgDn", IDC_NEXTGEN 453 | MENUITEM "&Previous generation\tPgUp", IDC_PREVGEN 454 | MENUITEM "&Update display\tCtrl+W", IDC_UPDATEDISPLAY 455 | END 456 | POPUP "&Options" 457 | BEGIN 458 | MENUITEM "&Period, rows, columns...\tF3", IDC_PERIODROWSCOLS 459 | MENUITEM "S&ymmetry...\tF4", IDC_SYMMETRY 460 | MENUITEM "&Translation...\tF5", IDC_TRANSLATE 461 | MENUITEM "&Search settings...\tF6", IDC_SEARCHSETTINGS 462 | MENUITEM "&Output settings...\tF7", IDC_OUTPUTSETTINGS 463 | MENUITEM "P&references...\tF8", IDC_PREFERENCES 464 | MENUITEM "&Diagonal selections\t/", IDC_SELECTMODE 465 | END 466 | POPUP "&Search" 467 | BEGIN 468 | MENUITEM "Pr&epare\tCtrl+P", IDC_SEARCHPREPARE 469 | MENUITEM "&Start\tCtrl+G", IDC_SEARCHSTART 470 | MENUITEM SEPARATOR 471 | MENUITEM "&Pause\tEsc", IDC_SEARCHPAUSE 472 | MENUITEM "&Continue\tSpace", IDC_SEARCHRESUME 473 | MENUITEM "&Backup\tB", IDC_SEARCHBACKUP 474 | MENUITEM "Backup 20\tY", IDC_SEARCHBACKUP2 475 | MENUITEM "Clear combination", IDC_CLEARCOMBINATION 476 | MENUITEM "&Reset\tCtrl+R", IDC_SEARCHRESET 477 | MENUITEM SEPARATOR 478 | MENUITEM "Copy result", IDC_COPYRESULT 479 | MENUITEM "Copy combination", IDC_COPYCOMBINATION 480 | END 481 | POPUP "&Help" 482 | BEGIN 483 | MENUITEM "&About", IDC_ABOUT 484 | END 485 | END 486 | 487 | ///////////////////////////////////////////////////////////////////////////// 488 | // 489 | // Accelerator 490 | // 491 | 492 | WLSACCEL ACCELERATORS DISCARDABLE 493 | BEGIN 494 | ",", IDC_PREVGEN, ASCII, NOINVERT 495 | ".", IDC_NEXTGEN, ASCII, NOINVERT 496 | "/", IDC_SELECTMODE, ASCII, NOINVERT 497 | "B", IDC_SEARCHBACKUP, VIRTKEY, NOINVERT 498 | "C", IDC_EDITCOPY, VIRTKEY, CONTROL, NOINVERT 499 | "D", IDC_SAVEGAME, VIRTKEY, NOINVERT 500 | "E", ID_FLIP_GEN_V, VIRTKEY, CONTROL, NOINVERT 501 | "E", ID_FLIP_ALL_V, VIRTKEY, SHIFT, CONTROL, 502 | NOINVERT 503 | "G", IDC_SEARCHSTART, VIRTKEY, CONTROL, NOINVERT 504 | "H", ID_FLIP_GEN_H, VIRTKEY, CONTROL, NOINVERT 505 | "H", ID_FLIP_ALL_H, VIRTKEY, SHIFT, CONTROL, 506 | NOINVERT 507 | "L", IDC_OPENSTATE, VIRTKEY, NOINVERT 508 | "N", IDC_NEXTGEN, VIRTKEY, NOINVERT 509 | "O", IDC_OPENSTATE, VIRTKEY, CONTROL, NOINVERT 510 | "P", IDC_PREVGEN, VIRTKEY, NOINVERT 511 | "P", IDC_SEARCHPREPARE, VIRTKEY, CONTROL, NOINVERT 512 | "R", IDC_SEARCHRESET, VIRTKEY, CONTROL, NOINVERT 513 | "S", IDC_SAVEGAME, VIRTKEY, CONTROL, NOINVERT 514 | "T", ID_TRANS_GEN, VIRTKEY, CONTROL, NOINVERT 515 | "T", ID_TRANS_ALL, VIRTKEY, SHIFT, CONTROL, 516 | NOINVERT 517 | "W", IDC_UPDATEDISPLAY, VIRTKEY, CONTROL, NOINVERT 518 | "Y", IDC_SEARCHBACKUP2, VIRTKEY, NOINVERT 519 | VK_SPACE, IDC_SEARCHRESUME, VIRTKEY, NOINVERT 520 | VK_BACK, ID_HIDESEL, VIRTKEY, NOINVERT 521 | VK_ESCAPE, IDC_SEARCHPAUSE, VIRTKEY, NOINVERT 522 | VK_F3, IDC_PERIODROWSCOLS, VIRTKEY, NOINVERT 523 | VK_F4, IDC_SYMMETRY, VIRTKEY, NOINVERT 524 | VK_F5, IDC_TRANSLATE, VIRTKEY, NOINVERT 525 | VK_F6, IDC_SEARCHSETTINGS, VIRTKEY, NOINVERT 526 | VK_F7, IDC_OUTPUTSETTINGS, VIRTKEY, NOINVERT 527 | VK_F8, IDC_PREFERENCES, VIRTKEY, NOINVERT 528 | VK_DELETE, IDC_CLEARGEN, VIRTKEY, CONTROL, NOINVERT 529 | VK_DELETE, IDC_CLEAR, VIRTKEY, SHIFT, CONTROL, 530 | NOINVERT 531 | VK_DOWN, IDC_SHIFTGDOWN, VIRTKEY, SHIFT, NOINVERT 532 | VK_DOWN, IDC_SHIFTADOWN, VIRTKEY, SHIFT, CONTROL, 533 | NOINVERT 534 | VK_LEFT, IDC_PREVGEN, VIRTKEY, NOINVERT 535 | VK_LEFT, IDC_SHIFTGLEFT, VIRTKEY, SHIFT, NOINVERT 536 | VK_LEFT, IDC_SHIFTALEFT, VIRTKEY, SHIFT, CONTROL, 537 | NOINVERT 538 | VK_NEXT, IDC_NEXTGEN, VIRTKEY, NOINVERT 539 | VK_NEXT, IDC_SHIFTAFUTURE, VIRTKEY, SHIFT, CONTROL, 540 | NOINVERT 541 | VK_PRIOR, IDC_PREVGEN, VIRTKEY, NOINVERT 542 | VK_PRIOR, IDC_SHIFTAPAST, VIRTKEY, SHIFT, CONTROL, 543 | NOINVERT 544 | VK_RIGHT, IDC_NEXTGEN, VIRTKEY, NOINVERT 545 | VK_RIGHT, IDC_SHIFTGRIGHT, VIRTKEY, SHIFT, NOINVERT 546 | VK_RIGHT, IDC_SHIFTARIGHT, VIRTKEY, SHIFT, CONTROL, 547 | NOINVERT 548 | VK_UP, IDC_SHIFTGUP, VIRTKEY, SHIFT, NOINVERT 549 | VK_UP, IDC_SHIFTAUP, VIRTKEY, SHIFT, CONTROL, 550 | NOINVERT 551 | END 552 | 553 | ///////////////////////////////////////////////////////////////////////////// 554 | // 555 | // Bitmap 556 | // 557 | 558 | IDB_SYM0 BITMAP DISCARDABLE "sym0.bmp" 559 | IDB_SYM1 BITMAP DISCARDABLE "sym1.bmp" 560 | IDB_SYM2 BITMAP DISCARDABLE "sym2.bmp" 561 | IDB_SYM3 BITMAP DISCARDABLE "sym3.bmp" 562 | IDB_SYM4 BITMAP DISCARDABLE "sym4.bmp" 563 | IDB_SYM5 BITMAP DISCARDABLE "sym5.bmp" 564 | IDB_SYM6 BITMAP DISCARDABLE "sym6.bmp" 565 | IDB_SYM7 BITMAP DISCARDABLE "sym7.bmp" 566 | IDB_SYM8 BITMAP DISCARDABLE "sym8.bmp" 567 | IDB_SYM9 BITMAP DISCARDABLE "sym9.bmp" 568 | IDB_TRANS0 BITMAP DISCARDABLE "trans0.bmp" 569 | IDB_TRANS1 BITMAP DISCARDABLE "trans1.bmp" 570 | IDB_TRANS2 BITMAP DISCARDABLE "trans2.bmp" 571 | IDB_TRANS3 BITMAP DISCARDABLE "trans3.bmp" 572 | IDB_TRANS4 BITMAP DISCARDABLE "trans4.bmp" 573 | IDB_TRANS5 BITMAP DISCARDABLE "trans5.bmp" 574 | IDB_TRANS6 BITMAP DISCARDABLE "trans6.bmp" 575 | IDB_TRANS7 BITMAP DISCARDABLE "trans7.bmp" 576 | 577 | ///////////////////////////////////////////////////////////////////////////// 578 | // 579 | // Icon 580 | // 581 | 582 | // Icon with lowest ID value placed first to ensure application icon 583 | // remains consistent on all systems. 584 | ICONWLS ICON DISCARDABLE "wls.ico" 585 | 586 | 587 | #endif // English (U.S.) resources 588 | ///////////////////////////////////////////////////////////////////////////// 589 | -------------------------------------------------------------------------------- /source/interact.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Life search program - user interactions module. 3 | * Author: David I. Bell. 4 | 5 | ****** Heavily modified. Modifications not noted consistently. -JES ****** 6 | */ 7 | 8 | #include "wls-config.h" 9 | #define _CRT_SECURE_NO_WARNINGS 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "lifesrc.h" 15 | #include 16 | 17 | extern struct globals_struct g; 18 | 19 | 20 | #ifdef JS 21 | 22 | 23 | /* 24 | * Local procedures 25 | */ 26 | static long getnum (char **, int); 27 | 28 | 29 | /* 30 | * Table of addresses of parameters which are loaded and saved. 31 | * Changing this table may invalidate old dump files, unless new 32 | * parameters are added at the end and default to zero. 33 | * When changed incompatibly, the dump file version should be incremented. 34 | * The table is ended with a NULL pointer. 35 | */ 36 | static int * param_table[] = 37 | { 38 | &g.curstatus, 39 | &g.nrows, &g.ncols, &g.period, &g.rowtrans, &g.coltrans, 40 | &g.rowsym, &g.colsym, &g.pointsym, &g.fwdsym, &g.bwdsym, 41 | &g.fliprows, &g.flipcols, &g.flipquads, 42 | &g.parent, &g.allobjects, &g.nearcols, &g.maxcount, 43 | &g.userow, &g.usecol, &g.colcells, &g.colwidth, &g.follow, 44 | &g.orderwide, &g.ordergens, &g.ordermiddle, &g.followgens, 45 | 46 | &g.sortorder, &g.symmetry, &g.trans_rotate, &g.trans_flip, &g.trans_x, &g.trans_y, 47 | &g.knightsort, 48 | NULL 49 | }; 50 | 51 | 52 | /* 53 | * Exclude all cells within the previous light cone centered at the 54 | * specified cell from searching. 55 | */ 56 | void 57 | excludecone(int row, int col, int gen) 58 | { 59 | int tgen; 60 | int trow; 61 | int tcol; 62 | int dist; 63 | 64 | for (tgen = g.period; tgen >= gen; tgen--) 65 | { 66 | dist = tgen - gen; 67 | 68 | for (trow = row - dist; trow <= row + dist; trow++) 69 | { 70 | for (tcol = col - dist; tcol <= col + dist; tcol++) 71 | { 72 | findcell(trow, tcol, tgen)->choose = FALSE; 73 | } 74 | } 75 | } 76 | } 77 | 78 | 79 | /* 80 | * Freeze all generations of the specified cell. 81 | * A frozen cell can be ON or OFF, but must be the same in all generations. 82 | * This routine marks them as frozen, and also inserts all the cells of 83 | * the generation into the same loop so that they will be forced 84 | * to have the same state. 85 | */ 86 | BOOL 87 | freezecell(int row, int col) 88 | { 89 | int gen; 90 | CELL * cell0; 91 | CELL * cell; 92 | STATUS ret; 93 | 94 | cell0 = findcell(row, col, 0); 95 | 96 | for (gen = 0; gen < g.period; gen++) 97 | { 98 | cell = findcell(row, col, gen); 99 | 100 | cell->frozen = TRUE; 101 | 102 | ret = loopcells(cell0, cell); 103 | if(!ret) return FALSE; 104 | } 105 | return TRUE; 106 | } 107 | 108 | /* 109 | * Backup the search to the nth latest free choice. 110 | * Notice: This skips examinination of some of the possibilities, thus 111 | * maybe missing a solution. Therefore this should only be used when it 112 | * is obvious that the current search state is useless. 113 | */ 114 | void 115 | getbackup(char *cp) 116 | { 117 | CELL * cell; 118 | STATE state; 119 | int count; 120 | int blankstoo; 121 | 122 | blankstoo = TRUE; 123 | #if 0 124 | blankstoo = FALSE; /* this doesn't work */ 125 | 126 | if (*cp == 'b') 127 | { 128 | blankstoo = TRUE; 129 | cp++; 130 | } 131 | #endif 132 | count = getnum(&cp, 0); 133 | 134 | if ((count <= 0) || *cp) 135 | { 136 | ttystatus(_T("Must back up at least one cell\n")); 137 | 138 | return; 139 | } 140 | 141 | while (count > 0) 142 | { 143 | cell = backup(); 144 | 145 | if (cell == NULL) 146 | { 147 | wlsUpdateAndShowTmpField(); 148 | ttystatus(_T("Backed up over all possibilities\n")); 149 | 150 | return; 151 | } 152 | 153 | state = 1 - cell->state; 154 | 155 | if (blankstoo || (state == ON)) 156 | count--; 157 | 158 | cell->state = UNK; 159 | 160 | if (!go(cell, state, FALSE)) 161 | { 162 | wlsUpdateAndShowTmpField(); 163 | ttystatus(_T("Backed up over all possibilities\n")); 164 | 165 | return; 166 | } 167 | } 168 | 169 | wlsUpdateAndShowTmpField(); 170 | } 171 | 172 | /* 173 | * Write the current generation to the specified file. 174 | * Empty rows and columns are not written. 175 | * Filename of "." means write to stdout. 176 | */ 177 | void wlsWriteCurrentFieldToFile_internal(const TCHAR *filename, BOOL append) 178 | /* BOOL append; TRUE to append instead of create */ 179 | { 180 | FILE * fp; 181 | CELL * cell; 182 | int row; 183 | int col; 184 | int ch; 185 | int minrow, maxrow, mincol, maxcol; 186 | 187 | if(!g.saveoutput && !g.outputcols) return; 188 | 189 | if (*filename == '\0') 190 | return; 191 | 192 | fp = stdout; 193 | 194 | if (_tcscmp(filename, _T("."))) 195 | fp = _tfopen(filename, append ? _T("a") : _T("w")); 196 | 197 | if (fp == NULL) 198 | { 199 | ttystatus(_T("Cannot create \x201c%s\x201d\n"), filename); 200 | 201 | return; 202 | } 203 | 204 | /* 205 | * First find the minimum bounds on the object. 206 | */ 207 | minrow = g.nrows; 208 | mincol = g.ncols; 209 | maxrow = 1; 210 | maxcol = 1; 211 | 212 | for (row = 1; row <= g.nrows; row++) 213 | { 214 | for (col = 1; col <= g.ncols; col++) 215 | { 216 | cell = findcell(row, col, g.curgen); 217 | 218 | if (cell->state == OFF) 219 | continue; 220 | 221 | if (row < minrow) 222 | minrow = row; 223 | 224 | if (row > maxrow) 225 | maxrow = row; 226 | 227 | if (col < mincol) 228 | mincol = col; 229 | 230 | if (col > maxcol) 231 | maxcol = col; 232 | } 233 | } 234 | 235 | if (minrow > maxrow) 236 | { 237 | minrow = 1; 238 | maxrow = 1; 239 | mincol = 1; 240 | maxcol = 1; 241 | } 242 | 243 | if (fp == stdout) 244 | fprintf(fp, "#\n"); 245 | 246 | /* 247 | * Now write out the bounded area. 248 | */ 249 | for (row = minrow; row <= maxrow; row++) 250 | { 251 | for (col = mincol; col <= maxcol; col++) 252 | { 253 | cell = findcell(row, col, g.curgen); 254 | 255 | switch (cell->state) 256 | { 257 | case OFF: ch = '.'; break; 258 | case ON: ch = '*'; break; 259 | case UNK: ch = 260 | (cell->choose ? '?' : 'X'); 261 | break; 262 | default: 263 | ttystatus(_T("Bad cell state")); 264 | fclose(fp); 265 | 266 | return; 267 | } 268 | 269 | fputc(ch, fp); 270 | } 271 | 272 | fputc('\n', fp); 273 | } 274 | 275 | if (append) 276 | fprintf(fp, "\n"); 277 | 278 | if ((fp != stdout) && fclose(fp)) 279 | { 280 | ttystatus(_T("Error writing \x201c%s\x201d\n"), filename); 281 | 282 | return; 283 | } 284 | 285 | g.writecount++; 286 | if (fp != stdout) { 287 | wlsStatusf(NULL,_T("\x201c%s\x201d written (%d)"),filename,g.writecount); 288 | } 289 | } 290 | 291 | /* 292 | * Dump the current state of the search in the specified file. 293 | */ 294 | void dumpstate_internal(const TCHAR *filename, BOOL reserved) 295 | { 296 | FILE * fp; 297 | CELL ** set; 298 | CELL * cell; 299 | int row; 300 | int col; 301 | int gen; 302 | int ** param; 303 | int g1; 304 | int x,y,z; 305 | char buf[80]; 306 | 307 | if (*filename == '\0') 308 | return; 309 | 310 | fp = _tfopen(filename, _T("w")); 311 | 312 | if (fp == NULL) 313 | { 314 | ttystatus(_T("Cannot create \x201c%s\x201d\n"), filename); 315 | 316 | return; 317 | } 318 | 319 | /* 320 | * Dump out the version so we can detect incompatible formats. 321 | */ 322 | fprintf(fp, "V %d\n", DUMPVERSION); 323 | 324 | 325 | /* write out the original configuration */ 326 | fprintf(fp, "%d %d %d\n", g.ncols,g.nrows,g.period); 327 | for(z=0;zrow, cell->col, 371 | cell->gen, cell->state, cell->free); 372 | } 373 | 374 | /* 375 | * Dump out those cells which are being excluded from the search. 376 | */ 377 | for (row = 1; row <= g.nrows; row++) 378 | for (col = 1; col < g.ncols; col++) 379 | for (gen = 0; gen < g.period; gen++) 380 | { 381 | cell = findcell(row, col, gen); 382 | 383 | if (cell->choose) 384 | continue; 385 | 386 | fprintf(fp, "X %d %d %d\n", row, col, gen); 387 | } 388 | 389 | /* 390 | * Dump out those cells in generation 0 which are frozen. 391 | * It isn't necessary to remember frozen cells in other 392 | * generations since they will be copied from generation 0. 393 | */ 394 | for (row = 1; row <= g.nrows; row++) 395 | for (col = 1; col < g.ncols; col++) 396 | { 397 | cell = findcell(row, col, 0); 398 | 399 | if (cell->frozen) 400 | fprintf(fp, "F %d %d\n", row, col); 401 | } 402 | 403 | for(g1=0;g1choose = FALSE; 611 | 612 | buf[0] = '\0'; 613 | fgets(buf, LINESIZE, fp); 614 | } 615 | 616 | /* 617 | * Handle frozen cells. 618 | */ 619 | while (buf[0] == 'F') 620 | { 621 | cp = &buf[1]; 622 | row = getnum(&cp, 0); 623 | col = getnum(&cp, 0); 624 | 625 | if(!freezecell(row, col)) { 626 | fclose(fp); 627 | return FALSE; 628 | } 629 | 630 | buf[0] = '\0'; 631 | fgets(buf, LINESIZE, fp); 632 | } 633 | 634 | while(buf[0]=='O') { 635 | cp=&buf[1]; 636 | g1=getnum(&cp,0); 637 | row = getnum(&cp, 0); 638 | col = getnum(&cp, 0); 639 | val=getnum(&cp,0); 640 | wlsSetCellVal_Safe(g.field,g1,row,col,val); 641 | 642 | buf[0] = '\0'; 643 | fgets(buf, LINESIZE, fp); 644 | } 645 | 646 | 647 | 648 | if (buf[0] != 'T') 649 | { 650 | ttystatus(_T("Missing table line in state file\n")); 651 | fclose(fp); 652 | 653 | return FALSE; 654 | } 655 | 656 | cp = &buf[1]; 657 | g.baseset = &g.settable[getnum(&cp, 0)]; 658 | g.nextset = &g.settable[getnum(&cp, 0)]; 659 | 660 | fgets(buf, LINESIZE, fp); 661 | 662 | if (buf[0] != 'E') 663 | { 664 | ttystatus(_T("Missing end of file line in state file\n")); 665 | fclose(fp); 666 | 667 | return FALSE; 668 | } 669 | 670 | if (fclose(fp)) 671 | { 672 | ttystatus(_T("Error reading \x201c%s\x201d\n"), filename); 673 | 674 | return FALSE; 675 | } 676 | 677 | wlsStatusf(NULL,_T("State loaded from \x201c%s\x201d\n"), filename); 678 | return TRUE; 679 | } 680 | 681 | 682 | #if 0 683 | 684 | //static BOOL setall; /* set all cells from initial file */ 685 | /* 686 | * Read a file containing initial settings for either gen 0 or the last gen. 687 | * If setall is TRUE, both the ON and the OFF cells will be set. 688 | * Returns OK on success, ERROR1 on error. 689 | */ 690 | static STATUS 691 | readfile(TCHAR *file) 692 | { 693 | FILE * fp; 694 | char * cp; 695 | char ch; 696 | int row; 697 | int col; 698 | int gen; 699 | STATE state; 700 | char buf[LINESIZE]; 701 | 702 | file = getstr(file, "Read initial object from file: "); 703 | 704 | if (*file == '\0') 705 | return OK; 706 | 707 | fp = _tfopen(file, _T("r")); 708 | 709 | if (fp == NULL) 710 | { 711 | ttystatus(_T("Cannot open \x201c%s\x201d\n"), file); 712 | 713 | return ERROR1; 714 | } 715 | 716 | gen = (parent ? (genmax - 1) : 0); 717 | row = 0; 718 | 719 | while (fgets(buf, LINESIZE, fp)) 720 | { 721 | row++; 722 | cp = buf; 723 | col = 0; 724 | 725 | while (*cp && (*cp != '\n')) 726 | { 727 | col++; 728 | ch = *cp++; 729 | 730 | switch (ch) 731 | { 732 | case '?': 733 | continue; 734 | 735 | case 'x': 736 | case 'X': 737 | excludecone(row, col, gen); 738 | continue; 739 | 740 | case '+': 741 | freezecell(row, col); 742 | continue; 743 | 744 | case '.': 745 | case ' ': 746 | if (!setall) 747 | continue; 748 | 749 | state = OFF; 750 | break; 751 | 752 | case 'O': 753 | case 'o': 754 | case '*': 755 | state = ON; 756 | break; 757 | 758 | default: 759 | ttystatus(_T("Bad file format in line %d\n"), 760 | row); 761 | fclose(fp); 762 | 763 | return ERROR1; 764 | } 765 | 766 | if (proceed(findcell(row, col, gen), state, FALSE)) 767 | { 768 | ttystatus(_T("Inconsistent state for cell %d %d\n"), 769 | row, col); 770 | fclose(fp); 771 | 772 | return ERROR1; 773 | } 774 | } 775 | } 776 | 777 | if (fclose(fp)) 778 | { 779 | ttystatus(_T("Error reading \x201c%s\x201d\n"), file); 780 | 781 | return ERROR1; 782 | } 783 | 784 | return OK; 785 | } 786 | #endif 787 | 788 | #if 0 789 | /* 790 | * Check a string for being NULL, and if so, ask the user to specify a 791 | * value for it. Returned string may be static and thus is overwritten 792 | * for each call. Leading spaces in the string are skipped over. 793 | */ 794 | static char * 795 | getstr(char *str, char *prompt) 796 | // char * str; /* string to check for NULLness */ 797 | // char * prompt; /* message to prompt with */ 798 | { 799 | static char buf[LINESIZE]; 800 | 801 | if ((str == NULL) || (*str == '\0')) 802 | { 803 | if (!ttyread(prompt, buf, LINESIZE)) 804 | { 805 | buf[0] = '\0'; 806 | 807 | return buf; 808 | } 809 | 810 | str = buf; 811 | } 812 | 813 | while (isblank(*str)) 814 | str++; 815 | 816 | return str; 817 | } 818 | #endif 819 | 820 | 821 | /* 822 | * Read a number from a string, eating any leading or trailing blanks. 823 | * Returns the value, and indirectly updates the string pointer. 824 | * Returns specified default if no number was found. 825 | */ 826 | static long 827 | getnum(char **cpp, int defnum) 828 | { 829 | char * cp; 830 | long num; 831 | BOOL isneg; 832 | 833 | isneg = FALSE; 834 | cp = *cpp; 835 | 836 | while (isblank(*cp)) 837 | cp++; 838 | 839 | if (*cp == '-') 840 | { 841 | cp++; 842 | isneg = TRUE; 843 | } 844 | 845 | if (!isdigit(*cp)) 846 | { 847 | *cpp = cp; 848 | 849 | return defnum; 850 | } 851 | 852 | num = 0; 853 | 854 | while (isdigit(*cp)) 855 | num = num * 10 + (*cp++ - '0'); 856 | 857 | if (isneg) 858 | num = -num; 859 | 860 | while (isblank(*cp)) 861 | cp++; 862 | 863 | *cpp = cp; 864 | 865 | return num; 866 | } 867 | 868 | 869 | /* 870 | * Parse a string and set the Life rules from it. 871 | * Returns TRUE on success, or FALSE on an error. 872 | * The rules can be "mmm,nnn", "mmm/nnn", "Bmmm,Snnn", "Bmmm/Snnn", 873 | * or a hex number in the Wolfram encoding. 874 | */ 875 | BOOL 876 | setrules(TCHAR *cp) 877 | { 878 | int i; 879 | unsigned int bits; 880 | 881 | for (i = 0; i < 9; i++) 882 | { 883 | g.bornrules[i] = OFF; 884 | g.liverules[i] = OFF; 885 | } 886 | 887 | if (*cp == '\0') 888 | return FALSE; 889 | 890 | /* 891 | * See if the string contains a comma or a slash. 892 | * If not, then assume Wolfram's hex format. 893 | */ 894 | if ((_tcschr(cp, ',') == NULL) && (_tcschr(cp, '/') == NULL)) 895 | { 896 | bits = 0; 897 | 898 | for (; *cp; cp++) 899 | { 900 | bits <<= 4; 901 | 902 | if ((*cp >= '0') && (*cp <= '9')) 903 | bits += *cp - '0'; 904 | else if ((*cp >= 'a') && (*cp <= 'f')) 905 | bits += *cp - 'a' + 10; 906 | else if ((*cp >= 'A') && (*cp <= 'F')) 907 | bits += *cp - 'A' + 10; 908 | else 909 | return FALSE; 910 | } 911 | 912 | if (i & ~0x3ff) 913 | return FALSE; 914 | 915 | for (i = 0; i < 9; i++) 916 | { 917 | if (bits & 0x01) 918 | g.bornrules[i] = ON; 919 | 920 | if (bits & 0x02) 921 | g.liverules[i] = ON; 922 | 923 | bits >>= 2; 924 | } 925 | } 926 | else 927 | { 928 | /* 929 | * It is in normal born/survive format. 930 | */ 931 | if ((*cp == 'b') || (*cp == 'B')) 932 | cp++; 933 | 934 | while ((*cp >= '0') && (*cp <= '8')) 935 | g.bornrules[*cp++ - '0'] = ON; 936 | 937 | if ((*cp != ',') && (*cp != '/')) 938 | return FALSE; 939 | 940 | cp++; 941 | 942 | if ((*cp == 's') || (*cp == 'S')) 943 | cp++; 944 | 945 | while ((*cp >= '0') && (*cp <= '8')) 946 | g.liverules[*cp++ - '0'] = ON; 947 | 948 | if (*cp) 949 | return FALSE; 950 | } 951 | 952 | /* 953 | * Construct the rule string for printouts and see if this 954 | * is the normal Life rule. 955 | */ 956 | cp = g.rulestring; 957 | 958 | *cp++ = 'B'; 959 | 960 | for (i = 0; i < 9; i++) 961 | { 962 | if (g.bornrules[i] == ON) 963 | *cp++ = '0' + i; 964 | } 965 | 966 | *cp++ = '/'; 967 | *cp++ = 'S'; 968 | 969 | for (i = 0; i < 9; i++) 970 | { 971 | if (g.liverules[i] == ON) 972 | *cp++ = '0' + i; 973 | } 974 | 975 | *cp = '\0'; 976 | 977 | g.islife = (_tcscmp(g.rulestring, _T("B3/S23")) == 0); 978 | 979 | return TRUE; 980 | } 981 | 982 | BOOL setrulesA(char *rulestringA) 983 | { 984 | #ifdef UNICODE 985 | WCHAR rulestringW[20]; 986 | StringCbPrintfW(rulestringW,sizeof(rulestringW),_T("%S"),rulestringA); 987 | return setrules(rulestringW); 988 | #else 989 | return setrules(rulestringA); 990 | #endif 991 | } 992 | 993 | #else // KS: 994 | 995 | /* 996 | * Local procedures 997 | */ 998 | static long getnum(char **, int); 999 | 1000 | /* 1001 | * Table of addresses of parameters which are loaded and saved. 1002 | * Changing this table may invalidate old dump files, unless new 1003 | * parameters are added at the end and default to zero. 1004 | * When changed incompatibly, the dump file version should be incremented. 1005 | * The table is ended with a NULL pointer. 1006 | */ 1007 | static int *param_table[] = 1008 | { 1009 | &g.curstatus, 1010 | &g.nrows, &g.ncols, &g.period, 1011 | &g.rowtrans, &g.coltrans, 1012 | &g.rowsym, &g.colsym, &g.pointsym, &g.fwdsym, &g.bwdsym, 1013 | &g.fliprows, &g.flipcols, &g.flipquads, 1014 | &g.parent, &g.allobjects, &g.nearcols, &g.maxcount, 1015 | &g.userow, &g.usecol, &g.colcells, &g.colwidth, &g.follow, 1016 | &g.orderwide, &g.ordergens, &g.ordermiddle, &g.followgens, 1017 | &g.sortorder, &g.symmetry, &g.trans_rotate, &g.trans_flip, &g.trans_x, &g.trans_y, 1018 | &g.knightsort, 1019 | &g.smart, &g.smartwindow, &g.smartthreshold, 1020 | &g.foundcount, 1021 | &g.combine, &g.combining, &g.combinedcells, &g.setcombinedcells, &g.differentcombinedcells, 1022 | NULL 1023 | }; 1024 | 1025 | /* 1026 | * Freeze all generations of the specified cell. 1027 | * A frozen cell can be ON or OFF, but must be the same in all generations. 1028 | * This routine marks them as frozen, and also inserts all the cells of 1029 | * the generation into the same loop so that they will be forced 1030 | * to have the same state. 1031 | */ 1032 | BOOL 1033 | freezecell(int row, int col) 1034 | { 1035 | int gen; 1036 | CELL * cell0; 1037 | CELL * cell; 1038 | 1039 | cell0 = findcell(row, col, 0); 1040 | 1041 | for (gen = 0; gen < g.period; gen++) 1042 | { 1043 | cell = findcell(row, col, gen); 1044 | 1045 | cell->frozen = TRUE; 1046 | 1047 | loopcells(cell0, cell); 1048 | } 1049 | return TRUE; 1050 | } 1051 | 1052 | /* 1053 | * Backup the search to the nth latest free choice. 1054 | * Notice: This skips examinination of some of the possibilities, thus 1055 | * maybe missing a solution. Therefore this should only be used when it 1056 | * is obvious that the current search state is useless. 1057 | */ 1058 | void 1059 | getbackup(char *cp) 1060 | { 1061 | CELL * cell; 1062 | STATE state; 1063 | int count; 1064 | 1065 | count = getnum(&cp, 0); 1066 | 1067 | if ((count <= 0) || *cp) 1068 | { 1069 | ttystatus(_T("Must back up at least one cell\n")); 1070 | 1071 | return; 1072 | } 1073 | 1074 | while (count > 0) 1075 | { 1076 | cell = backup(); 1077 | 1078 | if (cell == NULL) 1079 | { 1080 | wlsUpdateAndShowTmpField(); 1081 | ttystatus(_T("Backed up over all possibilities\n")); 1082 | 1083 | return; 1084 | } 1085 | 1086 | state = 1 - cell->state; 1087 | 1088 | if (state == ON) 1089 | count--; 1090 | 1091 | cell->state = UNK; 1092 | 1093 | if (!go(cell, state, FALSE)) 1094 | { 1095 | wlsUpdateAndShowTmpField(); 1096 | ttystatus(_T("Backed up over all possibilities\n")); 1097 | 1098 | return; 1099 | } 1100 | } 1101 | 1102 | wlsUpdateAndShowTmpField(); 1103 | } 1104 | 1105 | /* 1106 | * Write the current generation to the specified file. 1107 | * Empty rows and columns are not written. 1108 | * Filename of "." means write to stdout. 1109 | */ 1110 | void wlsWriteCurrentFieldToFile_internal(const TCHAR *filename, BOOL append) 1111 | /* BOOL append; TRUE to append instead of create */ 1112 | { 1113 | FILE * fp; 1114 | CELL * cell; 1115 | int row; 1116 | int col; 1117 | int gen; 1118 | int ch; 1119 | int minrow, maxrow, mincol, maxcol; 1120 | 1121 | if(!g.saveoutput && !g.outputcols) return; 1122 | 1123 | if (*filename == '\0') 1124 | return; 1125 | 1126 | fp = stdout; 1127 | 1128 | if (_tcscmp(filename, _T("."))) 1129 | fp = _tfopen(filename, append ? _T("a") : _T("w")); 1130 | 1131 | if (fp == NULL) 1132 | { 1133 | ttystatus(_T("Cannot create \x201c%s\x201d\n"), filename); 1134 | 1135 | return; 1136 | } 1137 | 1138 | /* 1139 | * First find the minimum bounds on the object. 1140 | */ 1141 | minrow = g.nrows; 1142 | mincol = g.ncols; 1143 | maxrow = -1; 1144 | maxcol = -1; 1145 | 1146 | for (gen=0; gen < g.period; gen++) 1147 | { 1148 | for (row = 1; row <= g.nrows; row++) 1149 | { 1150 | for (col = 1; col <= g.ncols; col++) 1151 | { 1152 | cell = findcell(row, col, gen); 1153 | 1154 | if (cell->state != OFF) 1155 | { 1156 | 1157 | if (row < minrow) 1158 | minrow = row; 1159 | 1160 | if (row > maxrow) 1161 | maxrow = row; 1162 | 1163 | if (col < mincol) 1164 | mincol = col; 1165 | 1166 | if (col > maxcol) 1167 | maxcol = col; 1168 | } 1169 | } 1170 | } 1171 | } 1172 | 1173 | if (minrow > maxrow) 1174 | { 1175 | minrow = 0; 1176 | maxrow = 0; 1177 | mincol = 0; 1178 | maxcol = 0; 1179 | } 1180 | 1181 | if (fp == stdout) 1182 | fprintf(fp, "#\n"); 1183 | 1184 | /* 1185 | * Now write out the bounded area. 1186 | */ 1187 | for (row = minrow; row <= maxrow; row++) 1188 | { 1189 | for (gen = 0; gen < (g.saveoutputallgen ? g.period : 1); gen++) 1190 | { 1191 | for (col = mincol; col <= maxcol; col++) 1192 | { 1193 | cell = findcell(row, col, gen); 1194 | switch (cell->state) 1195 | { 1196 | case OFF: 1197 | ch = '.'; 1198 | break; 1199 | 1200 | case ON: 1201 | ch = '*'; 1202 | break; 1203 | 1204 | case UNK: 1205 | ch = cell->unchecked ? 'X' : '?'; 1206 | break; 1207 | 1208 | default: 1209 | ttystatus(_T("Bad cell state")); 1210 | fclose(fp); 1211 | 1212 | return; 1213 | } 1214 | 1215 | fputc(ch, fp); 1216 | } 1217 | if (g.saveoutputallgen && (gen < g.period - 1)) fputs(" ... ", fp); 1218 | } 1219 | 1220 | fputc('\n', fp); 1221 | } 1222 | 1223 | if (append) 1224 | { 1225 | fprintf(fp, ".\n.\n"); 1226 | } 1227 | 1228 | if ((fp != stdout) && fclose(fp)) 1229 | { 1230 | ttystatus(_T("Error writing \x201c%s\x201d\n"), filename); 1231 | 1232 | return; 1233 | } 1234 | 1235 | g.writecount++; 1236 | if (fp != stdout) { 1237 | wlsStatusf(NULL,_T("\x201c%s\x201d written (%d)"),filename,g.writecount); 1238 | } 1239 | } 1240 | 1241 | /* 1242 | * Dump the current state of the search in the specified file. 1243 | */ 1244 | void dumpstate_internal(const TCHAR *filename, BOOL echo) 1245 | { 1246 | FILE * fp; 1247 | CELL ** set; 1248 | CELL * cell; 1249 | int row; 1250 | int col; 1251 | int gen; 1252 | int ** param; 1253 | char ind; 1254 | char buf[80]; 1255 | 1256 | fp = _tfopen(g.state_filename, _T("wt")); 1257 | 1258 | if (fp == NULL) 1259 | { 1260 | ttystatus(_T("Cannot create \x201c%s\x201d\n"), g.state_filename); 1261 | 1262 | return; 1263 | } 1264 | 1265 | /* 1266 | * Dump out the version so we can detect incompatible formats. 1267 | */ 1268 | fprintf(fp, "V %d\n", DUMPVERSION); 1269 | 1270 | /* 1271 | * Dump out the parameter values. 1272 | */ 1273 | fprintf(fp, "P"); 1274 | 1275 | for (param = param_table; *param; param++) 1276 | fprintf(fp, " %d", **param); 1277 | 1278 | fprintf(fp, "\n"); 1279 | 1280 | /* 1281 | * Dump out the life rule 1282 | */ 1283 | 1284 | #ifdef UNICODE 1285 | StringCbPrintfA(buf,sizeof(buf),"%S",g.rulestring); 1286 | fprintf(fp, "R %s\n", buf); 1287 | #else 1288 | fprintf(fp, "R %s\n", g.rulestring); 1289 | #endif 1290 | 1291 | /* write out the original configuration */ 1292 | 1293 | for(gen=0;gencombined != UNK)) 1312 | { 1313 | ind = (cell->combined == cell->state) ? '=' : '!'; 1314 | } 1315 | else 1316 | { 1317 | ind = '.'; 1318 | } 1319 | 1320 | fprintf(fp, "S %3d %3d %2d %d %d %c\n", cell->row, cell->col, 1321 | cell->gen, (cell->state == ON) ? 1 : 0, cell->free, ind); 1322 | } 1323 | 1324 | /* 1325 | * save combination 1326 | */ 1327 | 1328 | if (g.combining) 1329 | { 1330 | for(col=1;col<=g.ncols;col++) { 1331 | for(row=1;row<=g.nrows;row++) { 1332 | for(gen=0;gencombined != UNK) 1335 | { 1336 | fprintf(fp, "F %3d %3d %2d %d \n", row, col, gen, (cell->combined == ON) ? 1 : 0); 1337 | } 1338 | } 1339 | } 1340 | } 1341 | } 1342 | 1343 | /* 1344 | * end of file marker 1345 | */ 1346 | 1347 | fprintf(fp, "E\n"); 1348 | 1349 | if (fclose(fp)) 1350 | { 1351 | ttystatus(_T("Error writing \x201c%s\x201d\n"), g.state_filename); 1352 | 1353 | return; 1354 | } 1355 | 1356 | if (echo) 1357 | { 1358 | wlsStatusf(NULL,_T("State dumped to \x201c%s\x201d\n"), g.state_filename); 1359 | } 1360 | } 1361 | 1362 | /* 1363 | * Load a previously dumped state from a file. 1364 | * Warning: Almost no checks are made for validity of the state. 1365 | * Returns OK on success, ERROR1 on failure. 1366 | */ 1367 | BOOL loadstate_from_filename(const TCHAR *filename) 1368 | { 1369 | FILE * fp; 1370 | char * cp; 1371 | int row; 1372 | int col; 1373 | int gen; 1374 | STATE state; 1375 | BOOL free; 1376 | CELL * cell; 1377 | int ** param; 1378 | char buf[LINESIZE]; 1379 | int ver; 1380 | 1381 | STATUS status; 1382 | 1383 | 1384 | fp = _tfopen(filename, _T("r")); 1385 | 1386 | if (fp == NULL) 1387 | { 1388 | ttystatus(_T("Cannot open state file \x201c%s\x201d\n"), filename); 1389 | 1390 | return FALSE; 1391 | } 1392 | 1393 | //********************************************* 1394 | // Read and check the file version 1395 | //********************************************* 1396 | 1397 | buf[0] = '\0'; 1398 | fgets(buf, LINESIZE, fp); 1399 | 1400 | if (buf[0] != 'V') 1401 | { 1402 | ttystatus(_T("Missing version line in file \x201c%s\x201d\n"), filename); 1403 | fclose(fp); 1404 | 1405 | return FALSE; 1406 | } 1407 | 1408 | cp = &buf[1]; 1409 | ver = getnum(&cp, 0); 1410 | 1411 | if (DUMPVERSION != ver) 1412 | { 1413 | ttystatus(_T("Incorrect version of the dump file: expected %d, found %d"), DUMPVERSION, ver); 1414 | fclose(fp); 1415 | return FALSE; 1416 | } 1417 | 1418 | //********************************************* 1419 | // Read parameters 1420 | //********************************************* 1421 | 1422 | fgets(buf, LINESIZE, fp); 1423 | 1424 | /* 1425 | * Load up all of the parameters from the parameter line. 1426 | * If parameters are missing at the end, they are defaulted to zero. 1427 | */ 1428 | if (buf[0] != 'P') 1429 | { 1430 | ttystatus(_T("Missing parameter line in state file\n")); 1431 | fclose(fp); 1432 | 1433 | return FALSE; 1434 | } 1435 | 1436 | cp = &buf[1]; 1437 | 1438 | for (param = param_table; *param; param++) 1439 | **param = getnum(&cp, 0); 1440 | 1441 | wlsNewField(); 1442 | 1443 | //********************************************* 1444 | // Initialise 1445 | //********************************************* 1446 | 1447 | initcells(); 1448 | 1449 | //********************************************* 1450 | // Read life rule 1451 | //********************************************* 1452 | 1453 | fgets(buf, LINESIZE, fp); 1454 | 1455 | /* 1456 | * Set the life rules if they were specified. 1457 | * This line is optional. 1458 | */ 1459 | if (buf[0] != 'R') 1460 | { 1461 | ttystatus(_T("Missing rule line in state file\n")); 1462 | fclose(fp); 1463 | 1464 | return FALSE; 1465 | } 1466 | cp = &buf[strlen(buf) - 1]; 1467 | 1468 | if (*cp == '\n') *cp = '\0'; 1469 | 1470 | cp = &buf[1]; 1471 | 1472 | while (isblank(*cp)) cp++; 1473 | 1474 | if (!setrulesA(cp)) 1475 | { 1476 | ttystatus(_T("Bad Life rules in state file\n")); 1477 | fclose(fp); 1478 | 1479 | return FALSE; 1480 | } 1481 | 1482 | //********************************************* 1483 | // Read the initial state 1484 | //********************************************* 1485 | 1486 | for(gen=0;gencombined = state; 1585 | 1586 | buf[0] = '\0'; 1587 | fgets(buf, LINESIZE, fp); 1588 | 1589 | } 1590 | 1591 | //********************************************* 1592 | // Check the presence of the 'end' line 1593 | //********************************************* 1594 | 1595 | if (buf[0] != 'E') 1596 | { 1597 | ttystatus(_T("Missing end of file line in state file\n")); 1598 | fclose(fp); 1599 | 1600 | return FALSE; 1601 | } 1602 | 1603 | if (fclose(fp)) 1604 | { 1605 | ttystatus(_T("Error reading \x201c%s\x201d\n"), filename); 1606 | 1607 | return FALSE; 1608 | } 1609 | 1610 | wlsStatusf(NULL,_T("State loaded from \x201c%s\x201d\n"), filename); 1611 | return TRUE; 1612 | } 1613 | 1614 | /* 1615 | * Read a number from a string, eating any leading or trailing blanks. 1616 | * Returns the value, and indirectly updates the string pointer. 1617 | * Returns specified default if no number was found. 1618 | */ 1619 | static long 1620 | getnum(char **cpp, int defnum) 1621 | { 1622 | char * cp; 1623 | long num; 1624 | BOOL isneg; 1625 | 1626 | isneg = FALSE; 1627 | cp = *cpp; 1628 | 1629 | while (isblank(*cp)) 1630 | cp++; 1631 | 1632 | if (*cp == '-') 1633 | { 1634 | cp++; 1635 | isneg = TRUE; 1636 | } 1637 | 1638 | if (!isdigit(*cp)) 1639 | { 1640 | *cpp = cp; 1641 | 1642 | return defnum; 1643 | } 1644 | 1645 | num = 0; 1646 | 1647 | while (isdigit(*cp)) 1648 | num = num * 10 + (*cp++ - '0'); 1649 | 1650 | if (isneg) 1651 | num = -num; 1652 | 1653 | while (isblank(*cp)) 1654 | cp++; 1655 | 1656 | *cpp = cp; 1657 | 1658 | return num; 1659 | } 1660 | 1661 | 1662 | /* 1663 | * Parse a string and set the Life rules from it. 1664 | * Returns TRUE on success, or FALSE on an error. 1665 | * The rules can be "mmm,nnn", "mmm/nnn", "Bmmm,Snnn", "Bmmm/Snnn", 1666 | * or a hex number in the Wolfram encoding. 1667 | */ 1668 | BOOL 1669 | setrules(TCHAR *cp) 1670 | { 1671 | int i; 1672 | unsigned int bits; 1673 | 1674 | for (i = 0; i < 9; i++) 1675 | { 1676 | g.bornrules[i] = FALSE; 1677 | g.liverules[i] = FALSE; 1678 | } 1679 | 1680 | if (*cp == '\0') 1681 | return FALSE; 1682 | 1683 | /* 1684 | * See if the string contains a comma or a slash. 1685 | * If not, then assume Wolfram's hex format. 1686 | */ 1687 | if ((_tcschr(cp, ',') == NULL) && (_tcschr(cp, '/') == NULL)) 1688 | { 1689 | bits = 0; 1690 | 1691 | for (; *cp; cp++) 1692 | { 1693 | bits <<= 4; 1694 | 1695 | if ((*cp >= '0') && (*cp <= '9')) 1696 | bits += *cp - '0'; 1697 | else if ((*cp >= 'a') && (*cp <= 'f')) 1698 | bits += *cp - 'a' + 10; 1699 | else if ((*cp >= 'A') && (*cp <= 'F')) 1700 | bits += *cp - 'A' + 10; 1701 | else 1702 | return FALSE; 1703 | } 1704 | 1705 | if (i & ~0x3ff) 1706 | return FALSE; 1707 | 1708 | for (i = 0; i < 9; i++) 1709 | { 1710 | if (bits & 0x01) 1711 | g.bornrules[i] = TRUE; 1712 | 1713 | if (bits & 0x02) 1714 | g.liverules[i] = TRUE; 1715 | 1716 | bits >>= 2; 1717 | } 1718 | } 1719 | else 1720 | { 1721 | /* 1722 | * It is in normal born/survive format. 1723 | */ 1724 | if ((*cp == 'b') || (*cp == 'B')) 1725 | cp++; 1726 | 1727 | while ((*cp >= '0') && (*cp <= '8')) 1728 | g.bornrules[*cp++ - '0'] = TRUE; 1729 | 1730 | if ((*cp != ',') && (*cp != '/')) 1731 | return FALSE; 1732 | 1733 | cp++; 1734 | 1735 | if ((*cp == 's') || (*cp == 'S')) 1736 | cp++; 1737 | 1738 | while ((*cp >= '0') && (*cp <= '8')) 1739 | g.liverules[*cp++ - '0'] = TRUE; 1740 | 1741 | if (*cp) 1742 | return FALSE; 1743 | } 1744 | 1745 | /* 1746 | * Construct the rule string for printouts and see if this 1747 | * is the normal Life rule. 1748 | */ 1749 | cp = g.rulestring; 1750 | 1751 | *cp++ = 'B'; 1752 | 1753 | for (i = 0; i < 9; i++) 1754 | { 1755 | if (g.bornrules[i]) 1756 | *cp++ = '0' + i; 1757 | } 1758 | 1759 | *cp++ = '/'; 1760 | *cp++ = 'S'; 1761 | 1762 | for (i = 0; i < 9; i++) 1763 | { 1764 | if (g.liverules[i]) 1765 | *cp++ = '0' + i; 1766 | } 1767 | 1768 | *cp = '\0'; 1769 | 1770 | return TRUE; 1771 | } 1772 | 1773 | BOOL setrulesA(char *rulestringA) 1774 | { 1775 | #ifdef UNICODE 1776 | WCHAR rulestringW[WLS_RULESTRING_LEN]; 1777 | StringCbPrintfW(rulestringW,sizeof(rulestringW),_T("%S"),rulestringA); 1778 | return setrules(rulestringW); 1779 | #else 1780 | return setrules(rulestringA); 1781 | #endif 1782 | } 1783 | 1784 | #endif // JS/KS 1785 | -------------------------------------------------------------------------------- /source/search.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Life search program - actual search routines. 3 | * Author: David I. Bell. 4 | * Based on the algorithms by Dean Hickerson that were 5 | * included with the "xlife 2.0" distribution. Thanks! 6 | * Changes for arbitrary Life rules by Nathan S. Thompson. 7 | 8 | ****** Heavily modified. Modifications not noted consistently. -JES ****** 9 | */ 10 | 11 | #include "wls-config.h" 12 | #include 13 | #include 14 | #include 15 | #include "lifesrc.h" 16 | 17 | 18 | #define SUMCOUNT 8 19 | 20 | extern struct globals_struct g; 21 | 22 | extern volatile int abortthread; 23 | 24 | /* 25 | * The sort routine for searching. 26 | */ 27 | static int 28 | ordersortfunc(const void *xxx1, const void *xxx2) 29 | { 30 | CELL ** arg1; 31 | CELL ** arg2; 32 | CELL * c1; 33 | CELL * c2; 34 | int midcol; 35 | int midrow; 36 | int dif1; 37 | int dif2; 38 | int gen_diff; 39 | 40 | arg1=(CELL**)xxx1; 41 | arg2=(CELL**)xxx2; 42 | 43 | c1 = *arg1; 44 | c2 = *arg2; 45 | 46 | // Put generation 0 first 47 | // or if calculating parents, put generation 0 last 48 | gen_diff = 0; 49 | if (g.parent) { 50 | if (c1->gen < c2->gen) gen_diff = 1; 51 | if (c1->gen > c2->gen) gen_diff = -1; 52 | } 53 | else { 54 | if (c1->gen < c2->gen) gen_diff = -1; 55 | if (c1->gen > c2->gen) gen_diff = 1; 56 | } 57 | 58 | /* 59 | * If on equal position or not ordering by all generations 60 | * then sort primarily by generations 61 | */ 62 | if (((c1->row == c2->row) && (c1->col == c2->col)) || !g.ordergens) 63 | { 64 | if (gen_diff!=0) return gen_diff; 65 | // if we are here, it is the same cell 66 | } 67 | 68 | if(g.sortorder==SORTORDER_DIAG) { 69 | if(c1->col+c1->row > c2->col+c2->row) return 1; 70 | if(c1->col+c1->row < c2->col+c2->row) return -1; 71 | if(abs(c1->col-c1->row) > abs(c2->col-c2->row)) return (g.orderwide)?1:(-1); 72 | if(abs(c1->col-c1->row) < abs(c2->col-c2->row)) return (g.orderwide)?(-1):1; 73 | return gen_diff; 74 | } 75 | else if(g.knightsort || g.sortorder==SORTORDER_KNIGHT) { 76 | if(c1->col*2+c1->row > c2->col*2+c2->row) return 1; 77 | if(c1->col*2+c1->row < c2->col*2+c2->row) return -1; 78 | if(abs(c1->col-c1->row) > abs(c2->col-c2->row)) return (g.orderwide)?1:(-1); 79 | if(abs(c1->col-c1->row) < abs(c2->col-c2->row)) return (g.orderwide)?(-1):1; 80 | return gen_diff; 81 | } 82 | else if(g.sortorder==SORTORDER_TOPDOWN) { 83 | if(c1->row > c2->row) return 1; 84 | if(c1->row < c2->row) return -1; 85 | midcol = (g.ncols + 1) / 2; 86 | dif1 = abs(c1->col - midcol); 87 | dif2 = abs(c2->col - midcol); 88 | if (dif1 < dif2) return (g.orderwide ? -1 : 1); 89 | if (dif1 > dif2) return (g.orderwide ? 1 : -1); 90 | return gen_diff; 91 | } 92 | else if(g.sortorder==SORTORDER_CENTEROUT) { 93 | double midcolf, midrowf, d1, d2; 94 | midcolf = (1.0+(double)g.ncols) / 2.0; 95 | midrowf = (1.0+(double)g.nrows) / 2.0; 96 | d1 = (midcolf-(double)c1->col)*(midcolf-(double)c1->col) + 97 | (midrowf-(double)c1->row)*(midrowf-(double)c1->row); 98 | d2 = (midcolf-(double)c2->col)*(midcolf-(double)c2->col) + 99 | (midrowf-(double)c2->row)*(midrowf-(double)c2->row); 100 | if(d1>d2) return 1; 101 | if(d1col - midcol); 108 | dif2 = abs(c2->col - midcol); 109 | if (dif1 < dif2) return -1; 110 | if (dif1 > dif2) return 1; 111 | 112 | midrow = (g.nrows + 1) / 2; 113 | dif1 = abs(c1->row - midrow); 114 | dif2 = abs(c2->row - midrow); 115 | if (dif1 < dif2) return (g.orderwide ? -1 : 1); 116 | if (dif1 > dif2) return (g.orderwide ? 1 : -1); 117 | return gen_diff; 118 | } 119 | 120 | // else left-to-right sort order 121 | 122 | if (c1->col < c2->col) return -1; 123 | if (c1->col > c2->col) return 1; 124 | 125 | /* 126 | * Sort on the row number. 127 | * By default, this is from the middle row outwards. 128 | * But if wide ordering is set, the ordering is from the edge 129 | * inwards. Note that we actually set the ordering to be the 130 | * opposite of the desired order because the initial setting 131 | * for new cells is OFF. 132 | */ 133 | midrow = (g.nrows + 1) / 2; 134 | dif1 = abs(c1->row - midrow); 135 | dif2 = abs(c2->row - midrow); 136 | if (dif1 < dif2) return (g.orderwide ? -1 : 1); 137 | if (dif1 > dif2) return (g.orderwide ? 1 : -1); 138 | 139 | return gen_diff; 140 | } 141 | 142 | #ifdef JS 143 | 144 | // Uncommenting this line will disable certain features in an attempt 145 | // to make the search go a little faster. Maybe 10% faster or so... 146 | //#define FASTER 147 | 148 | 149 | /* 150 | * IMPLIC flag values. 151 | */ 152 | #define N0IC0 ((FLAGS) 0x01) /* new cell 0 ==> current cell 0 */ 153 | #define N0IC1 ((FLAGS) 0x02) /* new cell 0 ==> current cell 1 */ 154 | #define N1IC0 ((FLAGS) 0x04) /* new cell 1 ==> current cell 0 */ 155 | #define N1IC1 ((FLAGS) 0x08) /* new cell 1 ==> current cell 1 */ 156 | #define N0ICUN0 ((FLAGS) 0x10) /* new cell 0 ==> current unknown neighbors 0 */ 157 | #define N0ICUN1 ((FLAGS) 0x20) /* new cell 0 ==> current unknown neighbors 1 */ 158 | #define N1ICUN0 ((FLAGS) 0x40) /* new cell 1 ==> current unknown neighbors 0 */ 159 | #define N1ICUN1 ((FLAGS) 0x80) /* new cell 1 ==> current unknown neighbors 1 */ 160 | 161 | /* 162 | * Table of state values. 163 | */ 164 | static const STATE g_states[NSTATES] = {OFF, ON, UNK}; 165 | 166 | 167 | /* 168 | * Local procedures 169 | */ 170 | static void inittransit (void); 171 | static void initimplic (void); 172 | static void initsearchorder (void); 173 | static void linkcell (CELL *); 174 | static __inline STATE transition (STATE, int, int); 175 | static STATE choose (CELL *); 176 | static FLAGS implication (STATE, int, int); 177 | static CELL * symcell (CELL *); 178 | static CELL * mapcell (CELL *); 179 | static CELL * allocatecell (void); 180 | static CELL * getnormalunknown (void); 181 | static CELL * getaverageunknown (void); 182 | static STATUS consistify (CELL *); 183 | static STATUS consistify10 (CELL *); 184 | static STATUS examinenext (void); 185 | static BOOL checkwidth (CELL *); 186 | //static int getdesc (CELL *); 187 | //static int sumtodesc (STATE, int); 188 | static CELL * (*getunknown) (void); 189 | static __inline STATE nextstate (STATE, int); 190 | 191 | 192 | /* 193 | * Return the descriptor value for a cell and the sum of its neighbors. 194 | */ 195 | static __inline int 196 | sumtodesc(STATE state, int sum) 197 | { 198 | return ((sum & 0x88) ? (sum + state * 2 + 0x11) : (sum * 2 + state)); 199 | } 200 | 201 | 202 | /* 203 | * Initialize the table of cells. 204 | * Each cell in the active area is set to unknown state. 205 | * Boundary cells are set to zero state. 206 | */ 207 | BOOL 208 | initcells(void) 209 | { 210 | int row, col, gen; 211 | int i; 212 | STATUS ret; 213 | BOOL edge; 214 | CELL * cell; 215 | CELL * cell2; 216 | 217 | g.lifesrc_maxcells = (g.ncols + 2) * (g.nrows + 2) * g.period; 218 | g.newcellcount=0; 219 | g.auxcellcount=0; 220 | g.newcells=NULL; 221 | g.deadcell=NULL; 222 | g.searchlist=NULL; 223 | g.dummyrowinfo.oncount=0; 224 | g.dummycolinfo.oncount=0; 225 | 226 | g.settable = (CELL**)malloc(sizeof(CELL*)*g.lifesrc_maxcells); 227 | g.celltable = (CELL**)malloc(sizeof(CELL*)*g.lifesrc_maxcells); 228 | g.auxtable_alloc = TRANSMAX * (g.ncols + g.nrows + 4) * 2; 229 | g.auxtable = (CELL**)malloc(sizeof(CELL*)*g.auxtable_alloc); 230 | 231 | if ((g.nrows <= 0) || (g.nrows > ROWMAX) || 232 | (g.ncols <= 0) || (g.ncols > COLMAX) || 233 | (g.period <= 0) || (g.period > GENMAX) || 234 | (g.rowtrans < -TRANSMAX) || (g.rowtrans > TRANSMAX) || 235 | (g.coltrans < -TRANSMAX) || (g.coltrans > TRANSMAX)) 236 | { 237 | wlsErrorf(NULL,_T("ROW, COL, GEN, or TRANS out of range")); 238 | return FALSE; 239 | } 240 | 241 | /* 242 | * The first allocation of a cell MUST be deadcell. 243 | * Then allocate the cells in the cell table. 244 | */ 245 | g.deadcell = allocatecell(); 246 | 247 | for (i = 0; i < g.lifesrc_maxcells; i++) 248 | g.celltable[i] = allocatecell(); 249 | 250 | /* 251 | * Link the cells together. 252 | */ 253 | for (col = 0; col <= g.ncols+1; col++) 254 | { 255 | for (row = 0; row <= g.nrows+1; row++) 256 | { 257 | for (gen = 0; gen < g.period; gen++) 258 | { 259 | edge = ((row == 0) || (col == 0) || 260 | (row > g.nrows) || (col > g.ncols)); 261 | 262 | cell = findcell(row, col, gen); 263 | cell->gen = gen; 264 | cell->row = row; 265 | cell->col = col; 266 | cell->choose = TRUE; 267 | cell->rowinfo = &g.dummyrowinfo; 268 | cell->colinfo = &g.dummycolinfo; 269 | 270 | /* 271 | * If this is not an edge cell, then its state 272 | * is unknown and it needs linking to its 273 | * neighbors. 274 | */ 275 | if (!edge) 276 | { 277 | linkcell(cell); 278 | cell->state = UNK; 279 | cell->free = TRUE; 280 | } 281 | 282 | /* 283 | * Map time forwards and backwards, 284 | * wrapping around at the ends. 285 | */ 286 | cell->past = findcell(row, col, 287 | (gen+g.period-1) % g.period); 288 | 289 | cell->future = findcell(row, col, 290 | (gen+1) % g.period); 291 | 292 | /* 293 | * If this is not an edge cell, and 294 | * there is some symmetry, then put 295 | * this cell in the same loop as the 296 | * next symmetrical cell. 297 | */ 298 | // if ((rowsym || colsym || pointsym || 299 | // fwdsym || bwdsym) && !edge) 300 | if(g.symmetry) 301 | { 302 | ret = loopcells(cell, symcell(cell)); 303 | if(!ret) return FALSE; 304 | } 305 | } 306 | } 307 | } 308 | 309 | /* 310 | * If there is a non-standard mapping between the last generation 311 | * and the first generation, then change the future and past pointers 312 | * to implement it. This is for translations and flips. 313 | */ 314 | if (g.rowtrans || g.coltrans || g.fliprows || g.flipcols || g.flipquads) 315 | { 316 | for (row = 0; row <= g.nrows+1; row++) 317 | { 318 | for (col = 0; col <= g.ncols+1; col++) 319 | { 320 | /* cell = findcell(row, col, genmax - 1); 321 | cell2 = mapcell(cell); 322 | cell->future = cell2; 323 | cell2->past = cell; 324 | */ 325 | 326 | cell = findcell(row, col, 0); 327 | cell2 = mapcell(cell); 328 | cell->past = cell2; 329 | cell2->future = cell; 330 | } 331 | } 332 | } 333 | 334 | /* 335 | * Initialize the row and column info addresses for generation 0. 336 | */ 337 | for (row = 1; row <= g.nrows; row++) 338 | { 339 | for (col = 1; col <= g.ncols; col++) 340 | { 341 | cell = findcell(row, col, 0); 342 | cell->rowinfo = &g.rowinfo[row]; 343 | cell->colinfo = &g.colinfo[col]; 344 | } 345 | } 346 | 347 | initsearchorder(); 348 | 349 | if (g.follow) 350 | getunknown = getaverageunknown; 351 | else 352 | getunknown = getnormalunknown; 353 | 354 | g.newset = g.settable; 355 | g.nextset = g.settable; 356 | g.baseset = g.settable; 357 | 358 | g.curgen = 0; 359 | g.curstatus = OK; 360 | inittransit(); 361 | initimplic(); 362 | return TRUE; 363 | } 364 | 365 | /* 366 | * Order the cells to be searched by building the search table list. 367 | * This list is built backwards from the intended search order. 368 | * The default is to do searches from the middle row outwards, and 369 | * from the left to the right columns. The order can be changed though. 370 | */ 371 | static void 372 | initsearchorder(void) 373 | { 374 | int row, col, gen; 375 | int count; 376 | CELL * cell; 377 | CELL **table = NULL; 378 | int nrow,ncol; 379 | /* 380 | * Make a table of cells that will be searched. 381 | * Ignore cells that are not relevant to the search due to symmetry. 382 | */ 383 | table = (CELL**)malloc(sizeof(CELL*)*g.lifesrc_maxcells); 384 | count = 0; 385 | 386 | for (gen = 0; gen < g.period; gen++) 387 | for (col = 1; col <= g.ncols; col++) 388 | for (row = 1; row <= g.nrows; row++) { 389 | 390 | nrow=g.nrows+1-row; 391 | ncol=g.ncols+1-col; 392 | 393 | switch(g.symmetry) { 394 | case 1: 395 | if(col>ncol) continue; 396 | break; 397 | case 2: 398 | if(rownrow) continue; 402 | break; 403 | case 4: 404 | if(col>row) continue; 405 | break; 406 | case 5: 407 | if(col>ncol) continue; 408 | if(col==ncol && row>nrow) continue; 409 | break; 410 | case 6: 411 | if(col>ncol || row>nrow) continue; 412 | break; 413 | case 7: 414 | if(col>nrow || col>row) continue; 415 | break; 416 | case 8: 417 | if(col==ncol && row==nrow) break; // make sure we keep the center 418 | // if(colncol || row>=nrow) continue; 420 | break; 421 | case 9: 422 | // if(colrow || row>nrow) continue; 424 | break; 425 | } 426 | 427 | 428 | /* if (rowsym && (row * 2 > rowmax + 1)) 429 | continue; 430 | 431 | if (colsym && (col * 2 > colmax + 1)) 432 | continue; 433 | */ 434 | 435 | table[count++] = findcell(row, col, gen); 436 | 437 | } 438 | 439 | /* 440 | * Now sort the table based on our desired search order. 441 | */ 442 | qsort((char *) table, count, sizeof(CELL *), ordersortfunc); 443 | 444 | /* 445 | * Finally build the search list from the table elements in the 446 | * final order. 447 | */ 448 | g.searchlist = NULL; 449 | 450 | while (--count >= 0) 451 | { 452 | cell = table[count]; 453 | cell->search = g.searchlist; 454 | g.searchlist = cell; 455 | } 456 | 457 | g.fullsearchlist = g.searchlist; 458 | free(table); 459 | } 460 | 461 | 462 | 463 | 464 | /* 465 | * Set the state of a cell to the specified state. 466 | * The state is either ON or OFF. 467 | * Returns FALSE if the setting is inconsistent. 468 | * If the cell is newly set, then it is added to the set table. 469 | */ 470 | BOOL 471 | setcell(cell, state, free) 472 | CELL * cell; 473 | STATE state; 474 | BOOL free; 475 | { 476 | if (cell->state == state) 477 | { 478 | DPRINTF4("setcell %d %d %d to state %s already set\n", 479 | cell->row, cell->col, cell->gen, 480 | (state == ON) ? "on" : "off"); 481 | 482 | return TRUE; 483 | } 484 | 485 | if (cell->state != UNK) 486 | { 487 | DPRINTF4("setcell %d %d %d to state %s inconsistent\n", 488 | cell->row, cell->col, cell->gen, 489 | (state == ON) ? "on" : "off"); 490 | 491 | return FALSE; 492 | } 493 | 494 | if (cell->gen == 0) 495 | { 496 | if (g.usecol && (g.colinfo[g.usecol].oncount == 0) 497 | && (g.colinfo[g.usecol].setcount == g.nrows) && g.inited) 498 | { 499 | return FALSE; 500 | } 501 | 502 | if (state == ON) 503 | { 504 | 505 | #ifndef FASTER 506 | 507 | if (g.maxcount && (g.cellcount >= g.maxcount)) 508 | { 509 | DPRINTF2("setcell %d %d 0 on exceeds maxcount\n", 510 | cell->row, cell->col); 511 | 512 | return FALSE; 513 | } 514 | 515 | if (g.nearcols && (cell->near1 <= 0) && (cell->col > 1) 516 | && g.inited) 517 | { 518 | return FALSE; 519 | } 520 | 521 | if (g.colcells && (cell->colinfo->oncount >= g.colcells) 522 | && g.inited) 523 | { 524 | return FALSE; 525 | } 526 | 527 | if (g.colwidth && g.inited && checkwidth(cell)) 528 | return FALSE; 529 | 530 | if (g.nearcols) 531 | adjustnear(cell, 1); 532 | #endif 533 | 534 | cell->rowinfo->oncount++; 535 | cell->colinfo->oncount++; 536 | cell->colinfo->sumpos += cell->row; 537 | g.cellcount++; 538 | } 539 | } 540 | 541 | DPRINTF5("setcell %d %d %d to %s, %s successful\n", 542 | cell->row, cell->col, cell->gen, 543 | (free ? "free" : "forced"), ((state == ON) ? "on" : "off")); 544 | 545 | *g.newset++ = cell; 546 | 547 | cell->state = state; 548 | cell->free = free; 549 | cell->colinfo->setcount++; 550 | 551 | if ((cell->gen == 0) && (cell->colinfo->setcount == g.nrows)) 552 | g.fullcolumns++; 553 | 554 | return TRUE; 555 | } 556 | 557 | 558 | /* 559 | * Calculate the current descriptor for a cell. 560 | */ 561 | static __inline int 562 | getdesc(cell) 563 | CELL * cell; 564 | { 565 | int sum; 566 | /* 567 | switch(cell->specsym) { 568 | case 0: sum = cell->cul->state + cell->cu->state + cell->cur->state + 569 | cell->cdl->state + cell->cd->state + cell->cdr->state + 570 | cell->cl->state + cell->cr->state; 571 | break; 572 | 573 | case 1: // orthogonal, odd symmetry 574 | 575 | sum = 2*(cell->cdl->state + cell->cd->state + cell->cdr->state) + 576 | cell->cl->state + cell->cr->state; 577 | break; 578 | case 2: // orthogonal, even symmetry ???? 579 | sum = cell->cdl->state + cell->cd->state + cell->cdr->state + 580 | 2*(cell->cl->state + cell->cr->state) 581 | +cell->state; 582 | break; 583 | 584 | 585 | default: 586 | // should never get here 587 | exit(1); 588 | 589 | } 590 | if(cell->row==1) { 591 | sum = 2*(cell->cdl->state + cell->cd->state + cell->cdr->state) + 592 | cell->cl->state + cell->cr->state; 593 | } 594 | else { 595 | */ 596 | sum = cell->cul->state + cell->cu->state + cell->cur->state + 597 | cell->cdl->state + cell->cd->state + cell->cdr->state + 598 | cell->cl->state + cell->cr->state; 599 | /* } */ 600 | 601 | 602 | return ((sum & 0x88) ? (sum + cell->state * 2 + 0x11) : 603 | (sum * 2 + cell->state)); 604 | 605 | } 606 | 607 | 608 | 609 | 610 | /* 611 | * Consistify a cell. 612 | * This means examine this cell in the previous generation, and 613 | * make sure that the previous generation can validly produce the 614 | * current cell. Returns ERROR if the cell is inconsistent. 615 | */ 616 | static STATUS consistify(CELL *cell) 617 | { 618 | CELL * prevcell; 619 | int desc; 620 | STATE state; 621 | FLAGS flags; 622 | 623 | /* 624 | * If we are searching for parents and this is generation 0, then 625 | * the cell is consistent with respect to the previous generation. 626 | */ 627 | #ifndef FASTER 628 | if (g.parent && (cell->gen == 0)) 629 | return OK; 630 | #endif 631 | 632 | /* 633 | * First check the transit table entry for the previous 634 | * generation. Make sure that this cell matches the ON or 635 | * OFF state demanded by the transit table. If the current 636 | * cell is unknown but the transit table knows the answer, 637 | * then set the now known state of the cell. 638 | */ 639 | prevcell = cell->past; 640 | desc = getdesc(prevcell); 641 | state = g.transit[desc]; 642 | 643 | if ((state != UNK) && (state != cell->state)) 644 | { 645 | if (!setcell(cell, state, FALSE)) 646 | return ERROR1; 647 | } 648 | 649 | /* 650 | * Now look up the previous generation in the implic table. 651 | * If this cell implies anything about the cell or its neighbors 652 | * in the previous generation, then handle that. 653 | */ 654 | flags = g.implic[desc]; 655 | 656 | if ((flags == 0) || (cell->state == UNK)) 657 | return OK; 658 | 659 | DPRINTF1("Implication flags %x\n", flags); 660 | 661 | if ((flags & N0IC0) && (cell->state == OFF) && 662 | !setcell(prevcell, OFF, FALSE)) 663 | { 664 | return ERROR1; 665 | } 666 | 667 | if ((flags & N1IC1) && (cell->state == ON) && 668 | !setcell(prevcell, ON, FALSE)) 669 | { 670 | return ERROR1; 671 | } 672 | 673 | state = UNK; 674 | 675 | if (((flags & N0ICUN0) && (cell->state == OFF)) 676 | || ((flags & N1ICUN0) && (cell->state == ON))) 677 | { 678 | state = OFF; 679 | } 680 | 681 | if (((flags & N0ICUN1) && (cell->state == OFF)) 682 | || ((flags & N1ICUN1) && (cell->state == ON))) 683 | { 684 | state = ON; 685 | } 686 | 687 | if (state == UNK) 688 | { 689 | DPRINTF0("Implications successful\n"); 690 | 691 | return OK; 692 | } 693 | 694 | /* 695 | * For each unknown neighbor, set its state as indicated. 696 | * Return an error if any neighbor is inconsistent. 697 | */ 698 | DPRINTF4("Forcing unknown neighbors of cell %d %d %d %s\n", 699 | prevcell->row, prevcell->col, prevcell->gen, 700 | ((state == ON) ? "on" : "off")); 701 | 702 | // if(cell->specsym==0) { 703 | if ((prevcell->cul->state == UNK) && 704 | !setcell(prevcell->cul, state, FALSE)) return ERROR1; 705 | 706 | if ((prevcell->cu->state == UNK) && 707 | !setcell(prevcell->cu, state, FALSE)) return ERROR1; 708 | 709 | if ((prevcell->cur->state == UNK) && 710 | !setcell(prevcell->cur, state, FALSE)) return ERROR1; 711 | // } 712 | 713 | 714 | 715 | if ((prevcell->cl->state == UNK) && 716 | !setcell(prevcell->cl, state, FALSE)) return ERROR1; 717 | 718 | if ((prevcell->cr->state == UNK) && 719 | !setcell(prevcell->cr, state, FALSE)) return ERROR1; 720 | 721 | if ((prevcell->cdl->state == UNK) && 722 | !setcell(prevcell->cdl, state, FALSE)) return ERROR1; 723 | 724 | if ((prevcell->cd->state == UNK) && 725 | !setcell(prevcell->cd, state, FALSE)) return ERROR1; 726 | 727 | if ((prevcell->cdr->state == UNK) && 728 | !setcell(prevcell->cdr, state, FALSE)) return ERROR1; 729 | 730 | DPRINTF0("Implications successful\n"); 731 | 732 | return OK; 733 | } 734 | 735 | 736 | /* 737 | * See if a cell and its neighbors are consistent with the cell and its 738 | * neighbors in the next generation. 739 | */ 740 | static STATUS 741 | consistify10(cell) 742 | CELL * cell; 743 | { 744 | if (consistify(cell) != OK) 745 | return ERROR1; 746 | 747 | cell = cell->future; 748 | 749 | if (consistify(cell) != OK) 750 | return ERROR1; 751 | 752 | // if(cell->specsym==0) { 753 | if (consistify(cell->cul) != OK) return ERROR1; 754 | if (consistify(cell->cu) != OK) return ERROR1; 755 | if (consistify(cell->cur) != OK) return ERROR1; 756 | // } 757 | 758 | if (consistify(cell->cl) != OK) return ERROR1; 759 | if (consistify(cell->cr) != OK) return ERROR1; 760 | if (consistify(cell->cdl) != OK) return ERROR1; 761 | if (consistify(cell->cd) != OK) return ERROR1; 762 | if (consistify(cell->cdr) != OK) return ERROR1; 763 | 764 | return OK; 765 | } 766 | 767 | 768 | /* 769 | * Examine the next choice of cell settings. 770 | */ 771 | static STATUS 772 | examinenext(void) 773 | { 774 | CELL * cell; 775 | 776 | /* 777 | * If there are no more cells to examine, then what we have 778 | * is consistent. 779 | */ 780 | if (g.nextset == g.newset) 781 | return CONSISTENT; 782 | 783 | /* 784 | * Get the next cell to examine, and check it out for symmetry 785 | * and for consistency with its previous and next generations. 786 | */ 787 | cell = *g.nextset++; 788 | 789 | DPRINTF4("Examining saved cell %d %d %d (%s) for consistency\n", 790 | cell->row, cell->col, cell->gen, 791 | (cell->free ? "free" : "forced")); 792 | 793 | if (cell->loop && !setcell(cell->loop, cell->state, FALSE)) 794 | { 795 | return ERROR1; 796 | } 797 | 798 | return consistify10(cell); 799 | } 800 | 801 | 802 | /* 803 | * Set a cell to the specified value and determine all consequences we 804 | * can from the choice. Consequences are a contradiction or a consistency. 805 | */ 806 | BOOL 807 | proceed(cell, state, free) 808 | CELL * cell; 809 | STATE state; 810 | BOOL free; 811 | { 812 | int status; 813 | 814 | if (!setcell(cell, state, free)) 815 | return FALSE; 816 | 817 | for (;;) 818 | { 819 | status = examinenext(); 820 | 821 | if (status == ERROR1) 822 | return FALSE; 823 | 824 | if (status == CONSISTENT) 825 | return TRUE; 826 | } 827 | } 828 | 829 | 830 | /* 831 | * Back up the list of set cells to undo choices. 832 | * Returns the cell which is to be tried for the other possibility. 833 | * Returns NULL CELL on an "object cannot exist" error. 834 | */ 835 | CELL * 836 | backup(void) 837 | { 838 | CELL * cell; 839 | 840 | g.searchlist = g.fullsearchlist; 841 | 842 | while (g.newset != g.baseset) 843 | { 844 | cell = *--g.newset; 845 | 846 | DPRINTF5("backing up cell %d %d %d, was %s, %s\n", 847 | cell->row, cell->col, cell->gen, 848 | ((cell->state == ON) ? "on" : "off"), 849 | (cell->free ? "free": "forced")); 850 | 851 | if ((cell->state == ON) && (cell->gen == 0)) 852 | { 853 | cell->rowinfo->oncount--; 854 | cell->colinfo->oncount--; 855 | cell->colinfo->sumpos -= cell->row; 856 | g.cellcount--; 857 | adjustnear(cell, -1); 858 | } 859 | 860 | if ((cell->gen == 0) && (cell->colinfo->setcount == g.nrows)) 861 | g.fullcolumns--; 862 | 863 | cell->colinfo->setcount--; 864 | 865 | if (!cell->free) 866 | { 867 | cell->state = UNK; 868 | cell->free = TRUE; 869 | 870 | continue; 871 | } 872 | 873 | g.nextset = g.newset; 874 | 875 | return cell; 876 | } 877 | 878 | g.nextset = g.baseset; 879 | return NULL; 880 | } 881 | 882 | 883 | /* 884 | * Do checking based on setting the specified cell. 885 | * Returns ERROR if an inconsistency was found. 886 | */ 887 | BOOL 888 | go(cell, state, free) 889 | CELL * cell; 890 | STATE state; 891 | BOOL free; 892 | { 893 | for (;;) 894 | { 895 | if (proceed(cell, state, free)) return TRUE; 896 | 897 | cell = backup(); 898 | 899 | if (cell == NULL) return FALSE; 900 | 901 | free = FALSE; 902 | state = 1 - cell->state; 903 | cell->state = UNK; 904 | } 905 | } 906 | 907 | 908 | /* 909 | * Find another unknown cell in a normal search. 910 | * Returns NULL CELL if there are no more unknown cells. 911 | */ 912 | static CELL * 913 | getnormalunknown(void) 914 | { 915 | CELL * cell; 916 | 917 | for (cell = g.searchlist; cell; cell = cell->search) 918 | { 919 | if (!cell->choose) 920 | continue; 921 | 922 | if (cell->state == UNK) 923 | { 924 | g.searchlist = cell; 925 | 926 | return cell; 927 | } 928 | } 929 | 930 | return NULL; 931 | } 932 | 933 | 934 | /* 935 | * Find another unknown cell when averaging is done. 936 | * Returns NULL CELL if there are no more unknown cells. 937 | */ 938 | static CELL * 939 | getaverageunknown(void) 940 | { 941 | CELL * cell; 942 | CELL * bestcell; 943 | int bestdist; 944 | int curdist; 945 | int wantrow; 946 | int curcol; 947 | int testcol; 948 | 949 | bestcell = NULL; 950 | bestdist = -1; 951 | 952 | cell = g.searchlist; 953 | 954 | while (cell) 955 | { 956 | g.searchlist = cell; 957 | curcol = cell->col; 958 | 959 | testcol = curcol - 1; 960 | 961 | while ((testcol > 0) && (g.colinfo[testcol].oncount <= 0)) 962 | testcol--; 963 | 964 | if (testcol > 0) 965 | { 966 | wantrow = g.colinfo[testcol].sumpos / 967 | g.colinfo[testcol].oncount; 968 | } 969 | else 970 | wantrow = (g.nrows + 1) / 2; 971 | 972 | for (; cell && (cell->col == curcol); cell = cell->search) 973 | { 974 | if (!cell->choose) 975 | continue; 976 | 977 | if (cell->state == UNK) 978 | { 979 | curdist = cell->row - wantrow; 980 | 981 | if (curdist < 0) 982 | curdist = -curdist; 983 | 984 | if (curdist > bestdist) 985 | { 986 | bestcell = cell; 987 | bestdist = curdist; 988 | } 989 | } 990 | } 991 | 992 | if (bestcell) 993 | return bestcell; 994 | } 995 | 996 | return NULL; 997 | } 998 | 999 | 1000 | /* 1001 | * Choose a state for an unknown cell, either OFF or ON. 1002 | * Normally, we try to choose OFF cells first to terminate an object. 1003 | * But for follow generations mode, we try to choose the same setting 1004 | * as a nearby generation. 1005 | */ 1006 | static STATE 1007 | choose(cell) 1008 | CELL * cell; 1009 | { 1010 | /* 1011 | * If we are following cells in other generations, 1012 | * then try to do that. 1013 | */ 1014 | if (g.followgens) 1015 | { 1016 | if ((cell->past->state == ON) || 1017 | (cell->future->state == ON)) 1018 | { 1019 | return ON; 1020 | } 1021 | 1022 | if ((cell->past->state == OFF) || 1023 | (cell->future->state == OFF)) 1024 | { 1025 | return OFF; 1026 | } 1027 | } 1028 | 1029 | return OFF; 1030 | } 1031 | 1032 | 1033 | /* 1034 | * The top level search routine. 1035 | * Returns if an object is found, or is impossible. 1036 | */ 1037 | STATUS 1038 | search(void) 1039 | { 1040 | CELL * cell; 1041 | BOOL free; 1042 | BOOL needwrite; 1043 | STATE state; 1044 | 1045 | cell = (*getunknown)(); 1046 | 1047 | if (cell == NULL) 1048 | { 1049 | cell = backup(); 1050 | 1051 | if (cell == NULL) 1052 | return ERROR1; 1053 | 1054 | free = FALSE; 1055 | state = 1 - cell->state; 1056 | cell->state = UNK; 1057 | } 1058 | else { 1059 | #ifdef FASTER 1060 | state=OFF; 1061 | #else 1062 | state = choose(cell); 1063 | #endif 1064 | free = TRUE; 1065 | } 1066 | 1067 | for (;;) { 1068 | if(abortthread) return OK; 1069 | /* 1070 | * Set the state of the new cell. 1071 | */ 1072 | if (!go(cell, state, free)) 1073 | return NOTEXIST; 1074 | 1075 | /* 1076 | * If it is time to dump our state, then do that. 1077 | */ 1078 | if (g.dumpfreq && (++g.dumpcount >= g.dumpfreq)) 1079 | { 1080 | g.dumpcount = 0; 1081 | dumpstate(NULL, g.dumpfile, 0); 1082 | } 1083 | 1084 | /* 1085 | * If we have enough columns found, then remember to 1086 | * write it to the output file. Also keep the last 1087 | * columns count values up to date. 1088 | */ 1089 | needwrite = FALSE; 1090 | // needwrite1=FALSE; 1091 | 1092 | if (g.outputcols && 1093 | (g.fullcolumns >= g.outputlastcols + g.outputcols)) 1094 | { 1095 | g.outputlastcols = g.fullcolumns; 1096 | needwrite = TRUE; 1097 | } 1098 | 1099 | if (g.outputlastcols > g.fullcolumns) 1100 | g.outputlastcols = g.fullcolumns; 1101 | 1102 | /* 1103 | * If it is time to view the progress,then show it. 1104 | */ 1105 | if (needwrite || (g.viewfreq && (++g.viewcount >= g.viewfreq))) 1106 | { 1107 | wlsUpdateProgressCounter(); 1108 | wlsUpdateAndShowTmpField_Sync(); 1109 | g.viewcount = 0; 1110 | } 1111 | 1112 | /* 1113 | * Write the progress to the output file if needed. 1114 | * This is done after viewing it so that the write 1115 | * message will stay visible for a while. 1116 | */ 1117 | if (needwrite) 1118 | wlsWriteCurrentFieldToFile(NULL, g.outputfile, TRUE); 1119 | 1120 | /* 1121 | * Check for commands. 1122 | */ 1123 | // if (ttycheck()) 1124 | // getcommands(); 1125 | 1126 | /* 1127 | * Get the next unknown cell and choose its state. 1128 | */ 1129 | cell = (*getunknown)(); 1130 | 1131 | if (cell == NULL) 1132 | return FOUND; 1133 | 1134 | state = choose(cell); 1135 | free = TRUE; 1136 | } 1137 | } 1138 | 1139 | 1140 | /* 1141 | * Increment or decrement the near count in all the cells affected by 1142 | * this cell. This is done for all cells in the next columns which are 1143 | * within the distance specified the nearcols value. In this way, a 1144 | * quick test can be made to see if a cell is within range of another one. 1145 | */ 1146 | void 1147 | adjustnear(cell, inc) 1148 | CELL * cell; 1149 | int inc; 1150 | { 1151 | CELL * curcell; 1152 | int count; 1153 | int colcount; 1154 | 1155 | for (colcount = g.nearcols; colcount > 0; colcount--) 1156 | { 1157 | cell = cell->cr; 1158 | curcell = cell; 1159 | 1160 | for (count = g.nearcols; count-- >= 0; curcell = curcell->cu) 1161 | curcell->near1 += inc; 1162 | 1163 | curcell = cell->cd; 1164 | 1165 | for (count = g.nearcols; count-- > 0; curcell = curcell->cd) 1166 | curcell->near1 += inc; 1167 | } 1168 | } 1169 | 1170 | 1171 | /* 1172 | * Check to see if setting the specified cell ON would make the width of 1173 | * the column exceed the allowed value. For symmetric objects, the width 1174 | * is only measured from the center to an edge. Returns TRUE if the cell 1175 | * would exceed the value. 1176 | */ 1177 | static BOOL 1178 | checkwidth(cell) 1179 | CELL * cell; 1180 | { 1181 | int left; 1182 | int width; 1183 | int minrow; 1184 | int maxrow; 1185 | int srcminrow; 1186 | int srcmaxrow; 1187 | CELL * ucp; 1188 | CELL * dcp; 1189 | BOOL full; 1190 | 1191 | if (!g.colwidth || !g.inited || cell->gen) 1192 | return FALSE; 1193 | 1194 | left = cell->colinfo->oncount; 1195 | 1196 | if (left <= 0) 1197 | return FALSE; 1198 | 1199 | ucp = cell; 1200 | dcp = cell; 1201 | width = g.colwidth; 1202 | minrow = cell->row; 1203 | maxrow = cell->row; 1204 | srcminrow = 1; 1205 | srcmaxrow = g.nrows; 1206 | full = TRUE; 1207 | 1208 | if ((g.rowsym && (cell->col >= g.rowsym)) || 1209 | (g.fliprows && (cell->col >= g.fliprows))) 1210 | { 1211 | full = FALSE; 1212 | srcmaxrow = (g.nrows + 1) / 2; 1213 | 1214 | if (cell->row > srcmaxrow) 1215 | { 1216 | srcminrow = (g.nrows / 2) + 1; 1217 | srcmaxrow = g.nrows; 1218 | } 1219 | } 1220 | 1221 | while (left > 0) 1222 | { 1223 | if (full && (--width <= 0)) 1224 | return TRUE; 1225 | 1226 | ucp = ucp->cu; 1227 | dcp = dcp->cd; 1228 | 1229 | if (ucp->state == ON) 1230 | { 1231 | if (ucp->row >= srcminrow) 1232 | minrow = ucp->row; 1233 | 1234 | left--; 1235 | } 1236 | 1237 | if (dcp->state == ON) 1238 | { 1239 | if (dcp->row <= srcmaxrow) 1240 | maxrow = dcp->row; 1241 | 1242 | left--; 1243 | } 1244 | } 1245 | 1246 | if (maxrow - minrow >= g.colwidth) 1247 | return TRUE; 1248 | 1249 | return FALSE; 1250 | } 1251 | 1252 | 1253 | /* 1254 | * Check to see if any other generation is identical to generation 0. 1255 | * This is used to detect and weed out all objects with subperiods. 1256 | * (For example, stable objects or period 2 objects when using -g4.) 1257 | * Returns TRUE if there is an identical generation. 1258 | */ 1259 | BOOL 1260 | subperiods(void) 1261 | { 1262 | int row; 1263 | int col; 1264 | int gen; 1265 | CELL * cellg0; 1266 | CELL * cellgn; 1267 | 1268 | for (gen = 1; gen < g.period; gen++) 1269 | { 1270 | if (g.period % gen) 1271 | continue; 1272 | 1273 | for (row = 1; row <= g.nrows; row++) 1274 | { 1275 | for (col = 1; col <= g.ncols; col++) 1276 | { 1277 | cellg0 = findcell(row, col, 0); 1278 | cellgn = findcell(row, col, gen); 1279 | 1280 | if (cellg0->state != cellgn->state) 1281 | goto nextgen; 1282 | } 1283 | } 1284 | 1285 | return TRUE; 1286 | nextgen:; 1287 | } 1288 | 1289 | return FALSE; 1290 | } 1291 | 1292 | 1293 | /* 1294 | * Return the mapping of a cell from the last generation back to the first 1295 | * generation, or vice versa. This implements all flipping and translating 1296 | * of cells between these two generations. This routine should only be 1297 | * called for cells belonging to those two generations. 1298 | */ 1299 | static CELL * 1300 | mapcell(cell) 1301 | CELL * cell; 1302 | { 1303 | int row; 1304 | int col; 1305 | int tmp; 1306 | BOOL forward; 1307 | 1308 | row = cell->row; 1309 | col = cell->col; 1310 | forward = (cell->gen != 0); 1311 | 1312 | if (g.fliprows && (col >= g.fliprows)) 1313 | row = g.nrows + 1 - row; 1314 | 1315 | if (g.flipcols && (row >= g.flipcols)) 1316 | col = g.ncols + 1 - col; 1317 | 1318 | if (g.flipquads) 1319 | { /* NEED TO GO BACKWARDS */ 1320 | tmp = col; 1321 | col = row; 1322 | row = g.ncols + 1 - tmp; 1323 | } 1324 | 1325 | if (forward) 1326 | { 1327 | row += g.rowtrans; 1328 | col += g.coltrans; 1329 | } 1330 | else 1331 | { 1332 | row -= g.rowtrans; 1333 | col -= g.coltrans; 1334 | } 1335 | 1336 | if (forward) 1337 | return findcell(row, col, 0); 1338 | else 1339 | return findcell(row, col, g.period - 1); 1340 | } 1341 | 1342 | 1343 | /* 1344 | * Make the two specified cells belong to the same loop. 1345 | * If the two cells already belong to loops, the loops are joined. 1346 | * This will force the state of these two cells to follow each other. 1347 | * Symmetry uses this feature, and so does setting stable cells. 1348 | * If any cells in the loop are frozen, then they all are. 1349 | */ 1350 | BOOL loopcells(CELL *cell1, CELL *cell2) 1351 | { 1352 | CELL * cell; 1353 | BOOL frozen; 1354 | 1355 | if(cell2==NULL) return TRUE; 1356 | 1357 | /* 1358 | * Check simple cases of equality, or of either cell 1359 | * being the deadcell. 1360 | */ 1361 | if ((cell1 == g.deadcell) || (cell2 == g.deadcell)) 1362 | { 1363 | wlsErrorf(NULL,_T("Attemping to use deadcell in a loop")); 1364 | return FALSE; 1365 | } 1366 | 1367 | if (cell1 == cell2) 1368 | return TRUE; 1369 | 1370 | /* 1371 | * Make the cells belong to their own loop if required. 1372 | * This will simplify the code. 1373 | */ 1374 | if (cell1->loop == NULL) 1375 | cell1->loop = cell1; 1376 | 1377 | if (cell2->loop == NULL) 1378 | cell2->loop = cell2; 1379 | 1380 | /* 1381 | * See if the second cell is already part of the first cell's loop. 1382 | * If so, they they are already joined. We don't need to 1383 | * check the other direction. 1384 | */ 1385 | for (cell = cell1->loop; cell != cell1; cell = cell->loop) 1386 | { 1387 | if (cell == cell2) 1388 | return TRUE; 1389 | } 1390 | 1391 | /* 1392 | * The two cells belong to separate loops. 1393 | * Break each of those loops and make one big loop from them. 1394 | */ 1395 | cell = cell1->loop; 1396 | cell1->loop = cell2->loop; 1397 | cell2->loop = cell; 1398 | 1399 | /* 1400 | * See if any of the cells in the loop are frozen. 1401 | * If so, then mark all of the cells in the loop frozen 1402 | * since they effectively are anyway. This lets the 1403 | * user see that fact. 1404 | */ 1405 | frozen = cell1->frozen; 1406 | 1407 | for (cell = cell1->loop; cell != cell1; cell = cell->loop) 1408 | { 1409 | if (cell->frozen) 1410 | frozen = TRUE; 1411 | } 1412 | 1413 | if (frozen) 1414 | { 1415 | cell1->frozen = TRUE; 1416 | 1417 | for (cell = cell1->loop; cell != cell1; cell = cell->loop) 1418 | cell->frozen = TRUE; 1419 | } 1420 | return TRUE; 1421 | } 1422 | 1423 | 1424 | /* 1425 | * Return a cell which is symmetric to the given cell. 1426 | * It is not necessary to know all symmetric cells to a single cell, 1427 | * as long as all symmetric cells are chained in a loop. Thus a single 1428 | * pointer is good enough even for the case of both row and column symmetry. 1429 | * Returns NULL CELL if there is no symmetry. 1430 | */ 1431 | 1432 | 1433 | static CELL *symcell(CELL *cell) 1434 | { 1435 | int row; 1436 | int col; 1437 | int nrow; 1438 | int ncol; 1439 | 1440 | // if (!rowsym && !colsym && !pointsym && !fwdsym && !bwdsym) 1441 | // return NULL; 1442 | 1443 | if(!g.symmetry) 1444 | return NULL; 1445 | 1446 | row = cell->row; 1447 | col = cell->col; 1448 | nrow = g.nrows + 1 - row; 1449 | ncol = g.ncols + 1 - col; 1450 | 1451 | if(g.symmetry==1) // col sym 1452 | return findcell(row,ncol,cell->gen); 1453 | 1454 | if(g.symmetry==2) { // row sym 1455 | if(g.fastsym) { 1456 | if(g.nrows%2) { // odd sym 1457 | 1458 | if(abs(nrow-row)==2) { 1459 | return findcell(nrow,col,cell->gen); 1460 | } 1461 | if(row>nrow && cell->gen==0) cell->colinfo->setcount++; 1462 | 1463 | return NULL; 1464 | } 1465 | else { // even sym 1466 | if(abs(nrow-row)==1) { 1467 | return findcell(nrow,col,cell->gen); 1468 | } 1469 | if(nrow>row && cell->gen==0) cell->colinfo->setcount++; 1470 | return NULL; 1471 | } 1472 | 1473 | } 1474 | else { 1475 | return findcell(nrow,col,cell->gen); 1476 | } 1477 | } 1478 | 1479 | if(g.symmetry==3) // fwd diag 1480 | return findcell(ncol,nrow,cell->gen); 1481 | 1482 | if(g.symmetry==4) { // bwd diag 1483 | if(g.fastsym) { 1484 | if(abs(col-row)==1) 1485 | return findcell(col,row,cell->gen); 1486 | if(abs(col-row)==2) 1487 | return findcell(col,row,cell->gen); 1488 | return NULL; 1489 | } 1490 | return findcell(col,row,cell->gen); 1491 | } 1492 | 1493 | /* special symmetry 1494 | if(symmetry==4) { 1495 | if(col>=row) 1496 | return findcell(col,row-1,cell->gen); 1497 | else 1498 | return findcell(col+1,row,cell->gen); 1499 | 1500 | } 1501 | */ 1502 | if(g.symmetry==5) // origin 1503 | return findcell(nrow, ncol, cell->gen); 1504 | 1505 | if(g.symmetry==6) { 1506 | /* 1507 | * Here is there is both row and column symmetry. 1508 | * First see if the cell is in the middle row or middle column, 1509 | * and if so, then this is easy. 1510 | */ 1511 | if ((nrow == row) || (ncol == col)) 1512 | return findcell(nrow, ncol, cell->gen); 1513 | 1514 | /* 1515 | * The cell is really in one of the four quadrants, and therefore 1516 | * has four cells making up the symmetry. Link this cell to the 1517 | * symmetrical cell in the next quadrant clockwise. 1518 | */ 1519 | if ((row < nrow) == (col < ncol)) 1520 | return findcell(row, ncol, cell->gen); // quadrant 2 or 4 1521 | else 1522 | return findcell(nrow, col, cell->gen); // quadrant 1 or 3 1523 | } 1524 | 1525 | if(g.symmetry==7) { // diagonal 4-fold 1526 | // if on a diagonal... 1527 | if(row==col || row==ncol) 1528 | return findcell(nrow,ncol,cell->gen); 1529 | 1530 | // Not on a diagonal. 1531 | if((colgen); 1533 | else 1534 | return findcell(ncol,nrow,cell->gen); 1535 | } 1536 | 1537 | if(g.symmetry==8) { // origin*4 symmetry 1538 | // this is surprisingly simple 1539 | return findcell(ncol,row,cell->gen); 1540 | } 1541 | 1542 | if(g.symmetry==9) { // octagonal, this is gonna be tough 1543 | // if on an axis 1544 | if(nrow==row || ncol==col) 1545 | return findcell(ncol,row,cell->gen); 1546 | 1547 | // return findcell(nrow, ncol, cell->gen); 1548 | // if on a diagonal 1549 | if(row==col || row==ncol) 1550 | return findcell(ncol,row,cell->gen); 1551 | // return findcell(nrow,ncol,cell->gen); 1552 | 1553 | if((col>nrow && rownrow)) // octants 1,5 1554 | return findcell(nrow,col,cell->gen); // flip rows 1555 | if((colncol)||(col>nrow && colgen); // fwd diag 1557 | if((col>row && colncol)) // 3,7 1558 | return findcell(row,ncol,cell->gen); // flip cols 1559 | if((colrow && row>nrow)) // 4,8 1560 | return findcell(col,row,cell->gen); // bwd diag 1561 | 1562 | } 1563 | 1564 | return NULL; // crash if we get here :) 1565 | } 1566 | 1567 | /* 1568 | * Link a cell to its eight neighbors in the same generation, and also 1569 | * link those neighbors back to this cell. 1570 | */ 1571 | static void 1572 | linkcell(cell) 1573 | CELL * cell; 1574 | { 1575 | int row; 1576 | int col; 1577 | int gen; 1578 | CELL * paircell; 1579 | 1580 | row = cell->row; 1581 | col = cell->col; 1582 | gen = cell->gen; 1583 | 1584 | paircell = findcell(row - 1, col - 1, gen); 1585 | cell->cul = paircell; 1586 | paircell->cdr = cell; 1587 | 1588 | paircell = findcell(row - 1, col, gen); 1589 | cell->cu = paircell; 1590 | paircell->cd = cell; 1591 | 1592 | paircell = findcell(row - 1, col + 1, gen); 1593 | cell->cur = paircell; 1594 | paircell->cdl = cell; 1595 | 1596 | paircell = findcell(row, col - 1, gen); 1597 | cell->cl = paircell; 1598 | paircell->cr = cell; 1599 | 1600 | paircell = findcell(row, col + 1, gen); 1601 | cell->cr = paircell; 1602 | paircell->cl = cell; 1603 | 1604 | paircell = findcell(row + 1, col - 1, gen); 1605 | cell->cdl = paircell; 1606 | paircell->cur = cell; 1607 | 1608 | paircell = findcell(row + 1, col, gen); 1609 | cell->cd = paircell; 1610 | paircell->cu = cell; 1611 | 1612 | paircell = findcell(row + 1, col + 1, gen); 1613 | cell->cdr = paircell; 1614 | paircell->cul = cell; 1615 | } 1616 | 1617 | 1618 | /* 1619 | * Find a cell given its coordinates. 1620 | * Most coordinates range from 0 to colmax+1, 0 to rowmax+1, and 0 to genmax-1. 1621 | * Cells within this range are quickly found by indexing into celltable. 1622 | * Cells outside of this range are handled by searching an auxillary table, 1623 | * and are dynamically created as necessary. 1624 | */ 1625 | CELL * 1626 | findcell(row, col, gen) 1627 | int row; 1628 | int col; 1629 | int gen; 1630 | { 1631 | CELL * cell; 1632 | int i; 1633 | 1634 | /* 1635 | * If the cell is a normal cell, then we know where it is. 1636 | */ 1637 | if ((row >= 0) && (row <= g.nrows + 1) && 1638 | (col >= 0) && (col <= g.ncols + 1) && 1639 | (gen >= 0) && (gen < g.period)) 1640 | { 1641 | return g.celltable[(col * (g.nrows + 2) + row) * g.period + gen]; 1642 | } 1643 | 1644 | /* 1645 | * See if the cell is already allocated in the auxillary table. 1646 | */ 1647 | for (i = 0; i < g.auxcellcount; i++) 1648 | { 1649 | cell = g.auxtable[i]; 1650 | 1651 | if ((cell->row == row) && (cell->col == col) && 1652 | (cell->gen == gen)) 1653 | { 1654 | return cell; 1655 | } 1656 | } 1657 | 1658 | /* 1659 | * Need to allocate the cell and add it to the auxillary table. 1660 | */ 1661 | cell = allocatecell(); 1662 | cell->row = row; 1663 | cell->col = col; 1664 | cell->gen = gen; 1665 | cell->rowinfo = &g.dummyrowinfo; 1666 | cell->colinfo = &g.dummycolinfo; 1667 | 1668 | if(g.auxcellcount>=g.auxtable_alloc) { 1669 | // Ran out of space in the aux cell table. 1670 | g.auxtable_alloc *= 2; 1671 | g.auxtable = (CELL**)realloc(g.auxtable,sizeof(CELL*)*g.auxtable_alloc); 1672 | } 1673 | g.auxtable[g.auxcellcount++] = cell; 1674 | 1675 | return cell; 1676 | } 1677 | 1678 | 1679 | /* 1680 | * Allocate a new cell. 1681 | * The cell is initialized as if it was a boundary cell. 1682 | * Warning: The first allocation MUST be of the deadcell. 1683 | */ 1684 | static CELL * 1685 | allocatecell(void) 1686 | { 1687 | CELL * cell; 1688 | 1689 | /* 1690 | * Allocate a new chunk of cells if there are none left. 1691 | */ 1692 | if (g.newcellcount <= 0) 1693 | { 1694 | g.newcells = (CELL *) malloc(sizeof(CELL) * ALLOCSIZE); 1695 | 1696 | if (g.newcells == NULL) 1697 | { 1698 | wlsErrorf(NULL,_T("Cannot allocate cell structure")); 1699 | exit(1); 1700 | } 1701 | 1702 | record_malloc(1,(void*)g.newcells); 1703 | 1704 | g.newcellcount = ALLOCSIZE; 1705 | } 1706 | 1707 | g.newcellcount--; 1708 | cell = g.newcells++; 1709 | 1710 | /* 1711 | * If this is the first allocation, then make deadcell be this cell. 1712 | */ 1713 | if (g.deadcell == NULL) 1714 | g.deadcell = cell; 1715 | 1716 | /* 1717 | * Fill in the cell as if it was a boundary cell. 1718 | */ 1719 | cell->state = OFF; 1720 | cell->free = FALSE; 1721 | cell->frozen = FALSE; 1722 | cell->choose = TRUE; 1723 | cell->gen = -1; 1724 | cell->row = -1; 1725 | cell->col = -1; 1726 | cell->past = g.deadcell; 1727 | cell->future = g.deadcell; 1728 | cell->cul = g.deadcell; 1729 | cell->cu = g.deadcell; 1730 | cell->cur = g.deadcell; 1731 | cell->cl = g.deadcell; 1732 | cell->cr = g.deadcell; 1733 | cell->cdl = g.deadcell; 1734 | cell->cd = g.deadcell; 1735 | cell->cdr = g.deadcell; 1736 | cell->loop = NULL; 1737 | 1738 | //cell->specsym=0; 1739 | 1740 | return cell; 1741 | } 1742 | 1743 | 1744 | /* 1745 | * Initialize the implication table. 1746 | */ 1747 | static void 1748 | initimplic(void) 1749 | { 1750 | STATE state; 1751 | int OFFcount; 1752 | int ONcount; 1753 | int sum; 1754 | int desc; 1755 | int i; 1756 | 1757 | for (i = 0; i < NSTATES; i++) 1758 | { 1759 | state = g_states[i]; 1760 | 1761 | for (OFFcount = SUMCOUNT; OFFcount >= 0; OFFcount--) 1762 | { 1763 | for (ONcount = 0; ONcount + OFFcount <= SUMCOUNT; ONcount++) 1764 | { 1765 | sum = ONcount + (SUMCOUNT - ONcount - OFFcount) * UNK; 1766 | desc = sumtodesc(state, sum); 1767 | 1768 | g.implic[desc] = 1769 | implication(state, OFFcount, ONcount); 1770 | } 1771 | } 1772 | } 1773 | } 1774 | 1775 | 1776 | /* 1777 | * Initialize the transition table. 1778 | */ 1779 | static void 1780 | inittransit(void) 1781 | { 1782 | int state; 1783 | int OFFcount; 1784 | int ONcount; 1785 | int sum; 1786 | int desc; 1787 | int i; 1788 | 1789 | for (i = 0; i < NSTATES; i++) 1790 | { 1791 | state = g_states[i]; 1792 | 1793 | for (OFFcount = SUMCOUNT; OFFcount >= 0; OFFcount--) 1794 | { 1795 | for (ONcount = 0; ONcount + OFFcount <= SUMCOUNT; ONcount++) 1796 | { 1797 | sum = ONcount + (SUMCOUNT - ONcount - OFFcount) * UNK; 1798 | desc = sumtodesc((STATE)state, sum); 1799 | 1800 | g.transit[desc] = 1801 | transition(state, OFFcount, ONcount); 1802 | } 1803 | } 1804 | } 1805 | } 1806 | 1807 | 1808 | /* 1809 | * Return the next state if all neighbors are known. 1810 | */ 1811 | static __inline STATE 1812 | nextstate(state, ONcount) 1813 | STATE state; 1814 | int ONcount; 1815 | { 1816 | switch (state) 1817 | { 1818 | case ON: 1819 | return g.liverules[ONcount]; 1820 | 1821 | case OFF: 1822 | return g.bornrules[ONcount]; 1823 | 1824 | case UNK: 1825 | if (g.bornrules[ONcount] == g.liverules[ONcount]) 1826 | return g.bornrules[ONcount]; 1827 | 1828 | /* fall into default case */ 1829 | 1830 | default: 1831 | return UNK; 1832 | } 1833 | } 1834 | 1835 | 1836 | /* 1837 | * Determine the transition of a cell depending on its known neighbor counts. 1838 | * The unknown neighbor count is implicit since there are eight neighbors. 1839 | */ 1840 | static __inline STATE 1841 | transition(state, OFFcount, ONcount) 1842 | STATE state; 1843 | int OFFcount; 1844 | int ONcount; 1845 | { 1846 | BOOL on_always; 1847 | BOOL off_always; 1848 | int UNKcount; 1849 | int i; 1850 | 1851 | on_always = TRUE; 1852 | off_always = TRUE; 1853 | UNKcount = SUMCOUNT - OFFcount - ONcount; 1854 | 1855 | for (i = 0; i <= UNKcount; i++) 1856 | { 1857 | switch (nextstate(state, ONcount + i)) 1858 | { 1859 | case ON: 1860 | off_always = FALSE; 1861 | break; 1862 | 1863 | case OFF: 1864 | on_always = FALSE; 1865 | break; 1866 | 1867 | default: 1868 | return UNK; 1869 | } 1870 | } 1871 | 1872 | if (on_always) 1873 | return ON; 1874 | 1875 | if (off_always) 1876 | return OFF; 1877 | 1878 | return UNK; 1879 | } 1880 | 1881 | 1882 | /* 1883 | * Determine the implications of a cell depending on its known neighbor counts. 1884 | * The unknown neighbor count is implicit since there are eight neighbors. 1885 | */ 1886 | static FLAGS 1887 | implication(state, OFFcount, ONcount) 1888 | STATE state; 1889 | int OFFcount; 1890 | int ONcount; 1891 | { 1892 | FLAGS flags; 1893 | STATE next; 1894 | int UNKcount; 1895 | int i; 1896 | 1897 | UNKcount = SUMCOUNT - OFFcount - ONcount; 1898 | flags = 0; 1899 | 1900 | if (state == UNK) 1901 | { 1902 | flags |= (N0IC0 | N0IC1 | N1IC0 | N1IC1); /* set them all and */ 1903 | 1904 | for (i = 0; i <= UNKcount; i++) 1905 | { /* look for contradictions */ 1906 | next = nextstate(OFF, ONcount + i); 1907 | 1908 | if (next == ON) 1909 | flags &= ~N1IC1; 1910 | else if (next == OFF) 1911 | flags &= ~N0IC1; 1912 | 1913 | next = nextstate(ON, ONcount + i); 1914 | 1915 | if (next == ON) 1916 | flags &= ~N1IC0; 1917 | else if (next == OFF) 1918 | flags &= ~N0IC0; 1919 | } 1920 | } 1921 | 1922 | if (UNKcount) 1923 | { 1924 | flags |= (N0ICUN0 | N0ICUN1 | N1ICUN0 | N1ICUN1); 1925 | 1926 | if ((state == OFF) || (state == UNK)) 1927 | { 1928 | next = nextstate(OFF, ONcount); /* try unknowns zero */ 1929 | 1930 | if (next == ON) 1931 | flags &= ~N1ICUN1; 1932 | else if (next == OFF) 1933 | flags &= ~N0ICUN1; 1934 | 1935 | next = nextstate(OFF, ONcount + UNKcount); /* try all ones */ 1936 | 1937 | if (next == ON) 1938 | flags &= ~N1ICUN0; 1939 | else if (next == OFF) 1940 | flags &= ~N0ICUN0; 1941 | } 1942 | 1943 | if ((state == ON) || (state == UNK)) 1944 | { 1945 | next = nextstate(ON, ONcount); /* try unknowns zero */ 1946 | 1947 | if (next == ON) 1948 | flags &= ~N1ICUN1; 1949 | else if (next == OFF) 1950 | flags &= ~N0ICUN1; 1951 | 1952 | next = nextstate(ON, ONcount + UNKcount); /* try all ones */ 1953 | 1954 | if (next == ON) 1955 | flags &= ~N1ICUN0; 1956 | else if (next == OFF) 1957 | flags &= ~N0ICUN0; 1958 | } 1959 | 1960 | for (i = 1; i <= UNKcount - 1; i++) 1961 | { 1962 | if ((state == OFF) || (state == UNK)) 1963 | { 1964 | next = nextstate(OFF, ONcount + i); 1965 | 1966 | if (next == ON) 1967 | flags &= ~(N1ICUN0 | N1ICUN1); 1968 | else if (next == OFF) 1969 | flags &= ~(N0ICUN0 | N0ICUN1); 1970 | } 1971 | 1972 | if ((state == ON) || (state == UNK)) 1973 | { 1974 | next = nextstate(ON, ONcount + i); 1975 | 1976 | if (next == ON) 1977 | flags &= ~(N1ICUN0 | N1ICUN1); 1978 | else if (next == OFF) 1979 | flags &= ~(N0ICUN0 | N0ICUN1); 1980 | } 1981 | } 1982 | } 1983 | 1984 | return flags; 1985 | } 1986 | 1987 | #else // KS: 1988 | 1989 | 1990 | /* 1991 | * IMPLIC flag values. 1992 | */ 1993 | //typedef unsigned char FLAGS; 1994 | #define IMPBAD ((FLAGS) 0x00) // the cell state is inconsistent 1995 | #define IMPUN ((FLAGS) 0x01) // change unknown neighbors (there are some) 1996 | #define IMPUN1 ((FLAGS) 0x02) // change unknown neighbors to 1 (if not set, then change to 0) 1997 | #define IMPC ((FLAGS) 0x04) // change current cell (it is unknown) 1998 | #define IMPC1 ((FLAGS) 0x08) // change current cell to 1 (if not set, change it to 0) 1999 | #define IMPN ((FLAGS) 0x10) // change new cell (it is unknown) 2000 | #define IMPN1 ((FLAGS) 0x20) // change new cell to 1 (if not set, change it to 0) 2001 | #define IMPVOID ((FLAGS) 0x40) // invalid/unset implication 2002 | #define IMPOK ((FLAGS) 0x80) // valid state 2003 | 2004 | 2005 | /* 2006 | * Local procedures 2007 | */ 2008 | static void initimplic(void); 2009 | static void linkcell(CELL *); 2010 | static STATE choose(CELL *); 2011 | static CELL * symcell(CELL *); 2012 | static CELL * mapcell(CELL *); 2013 | static CELL * allocatecell(void); 2014 | static CELL * getnormalunknown(void); 2015 | static CELL * getaverageunknown(void); 2016 | static CELL * getsmartunknown(void); // KAS 2017 | static BOOL consistify(CELL *); 2018 | static BOOL consistify10(CELL *); 2019 | static BOOL checkwidth(CELL *); 2020 | static CELL * (*getunknown)(void); 2021 | 2022 | /* 2023 | * Initialize the table of cells. 2024 | * Each cell in the active area is set to unknown state. 2025 | * Boundary cells are set to zero state. 2026 | */ 2027 | BOOL 2028 | initcells(void) 2029 | { 2030 | int row, col, gen; 2031 | int i; 2032 | BOOL edge; 2033 | CELL * cell; 2034 | CELL * cell2; 2035 | 2036 | g.inited = FALSE; 2037 | 2038 | g.lifesrc_maxcells = (g.ncols + 2) * (g.nrows + 2) * g.period; 2039 | g.newcellcount=0; 2040 | g.auxcellcount=0; 2041 | g.newcells=NULL; 2042 | g.searchlist=NULL; 2043 | g.dummyrowinfo.oncount=0; 2044 | g.dummycolinfo.oncount=0; 2045 | 2046 | g.settable = (CELL**)malloc(sizeof(CELL*)*g.lifesrc_maxcells); 2047 | g.celltable = (CELL**)malloc(sizeof(CELL*)*g.lifesrc_maxcells); 2048 | g.auxtable_alloc = TRANSMAX * (g.ncols + g.nrows + 4) * 2; 2049 | g.auxtable = (CELL**)malloc(sizeof(CELL*)*g.auxtable_alloc); 2050 | g.searchtable = (CELL**)malloc(sizeof(CELL*)*g.lifesrc_maxcells); 2051 | 2052 | if ((g.nrows <= 0) || (g.nrows > ROWMAX) || 2053 | (g.ncols <= 0) || (g.ncols > COLMAX) || 2054 | (g.period <= 0) || (g.period > GENMAX) || 2055 | (g.rowtrans < -TRANSMAX) || (g.rowtrans > TRANSMAX) || 2056 | (g.coltrans < -TRANSMAX) || (g.coltrans > TRANSMAX)) 2057 | { 2058 | wlsErrorf(NULL,_T("ROW, COL, GEN, or TRANS out of range")); 2059 | return FALSE; 2060 | } 2061 | 2062 | for (i = 0; i < g.lifesrc_maxcells; i++) 2063 | g.celltable[i] = allocatecell(); 2064 | 2065 | /* 2066 | * Link the cells together. 2067 | */ 2068 | for (col = 0; col <= g.ncols+1; col++) 2069 | { 2070 | for (row = 0; row <= g.nrows+1; row++) 2071 | { 2072 | for (gen = 0; gen < g.period; gen++) 2073 | { 2074 | edge = ((row == 0) || (col == 0) || 2075 | (row > g.nrows) || (col > g.ncols)); 2076 | 2077 | cell = findcell(row, col, gen); 2078 | cell->gen = gen; 2079 | cell->row = row; 2080 | cell->col = col; 2081 | cell->rowinfo = &g.dummyrowinfo; 2082 | cell->colinfo = &g.dummycolinfo; 2083 | 2084 | cell->active = TRUE; 2085 | cell->unchecked = FALSE; 2086 | 2087 | /* 2088 | * If this is not an edge cell, then its state 2089 | * is unknown and it needs linking to its 2090 | * neighbors. 2091 | */ 2092 | if (!edge) 2093 | { 2094 | linkcell(cell); 2095 | cell->state = UNK; 2096 | cell->combined = UNK; 2097 | cell->free = TRUE; 2098 | } 2099 | 2100 | /* 2101 | * Map time forwards and backwards, 2102 | * wrapping around at the ends. 2103 | */ 2104 | cell->past = findcell(row, col, 2105 | (gen+g.period-1) % g.period); 2106 | 2107 | cell->future = findcell(row, col, 2108 | (gen+1) % g.period); 2109 | 2110 | /* 2111 | * If this is not an edge cell, and 2112 | * there is some symmetry, then put 2113 | * this cell in the same loop as the 2114 | * next symmetrical cell. 2115 | */ 2116 | if(g.symmetry && !edge) 2117 | { 2118 | loopcells(cell, symcell(cell)); 2119 | } 2120 | } 2121 | } 2122 | } 2123 | 2124 | /* 2125 | * Now for the symmetry 2126 | * Let's look for all loops 2127 | * and select one cell from each loop as active 2128 | */ 2129 | 2130 | for (col = 1; col <= g.ncols; col++) { 2131 | for (row = 1; row <= g.nrows; row++) { 2132 | for (gen = 0; gen < g.period; gen++) { 2133 | cell = findcell(row, col, gen); 2134 | 2135 | if (cell->active) { 2136 | cell2 = cell->loop; 2137 | while (cell2 != cell) { 2138 | cell2->active = FALSE; 2139 | cell2 = cell2->loop; 2140 | } 2141 | } 2142 | } 2143 | } 2144 | } 2145 | 2146 | 2147 | /* 2148 | * If there is a non-standard mapping between the last generation 2149 | * and the first generation, then change the future and past pointers 2150 | * to implement it. This is for translations and flips. 2151 | */ 2152 | if (g.rowtrans || g.coltrans || g.fliprows || g.flipcols || g.flipquads) 2153 | { 2154 | for (row = 0; row <= g.nrows+1; row++) 2155 | { 2156 | for (col = 0; col <= g.ncols+1; col++) 2157 | { 2158 | cell = findcell(row, col, 0); 2159 | cell2 = mapcell(cell); 2160 | cell->past = cell2; 2161 | cell2->future = cell; 2162 | } 2163 | } 2164 | } 2165 | 2166 | /* 2167 | * Initialize the row and column info addresses for generation 0. 2168 | */ 2169 | for (row = 1; row <= g.nrows; row++) 2170 | { 2171 | for (col = 1; col <= g.ncols; col++) 2172 | { 2173 | cell = findcell(row, col, 0); 2174 | cell->rowinfo = &g.rowinfo[row]; 2175 | cell->colinfo = &g.colinfo[col]; 2176 | } 2177 | } 2178 | 2179 | if (g.smart) { 2180 | getunknown = getsmartunknown; // KAS 2181 | } else if (g.follow) { 2182 | getunknown = getaverageunknown; 2183 | } else { 2184 | getunknown = getnormalunknown; 2185 | } 2186 | 2187 | g.newset = g.settable; 2188 | g.nextset = g.settable; 2189 | 2190 | g.searchset = g.searchtable; 2191 | 2192 | g.curstatus = OK; 2193 | initimplic(); 2194 | 2195 | g.inited = TRUE; 2196 | return TRUE; 2197 | } 2198 | 2199 | /* 2200 | * Order the cells to be searched by building the search table list. 2201 | * This list is built backwards from the intended search order. 2202 | * The default is to do searches from the middle row outwards, and 2203 | * from the left to the right columns. The order can be changed though. 2204 | */ 2205 | void 2206 | initsearchorder(void) 2207 | { 2208 | int row, col, gen; 2209 | int count; 2210 | CELL * cell; 2211 | CELL **table = NULL; 2212 | /* 2213 | * Make a table of cells that will be searched. 2214 | * Ignore cells that are not relevant to the search due to symmetry. 2215 | */ 2216 | table = (CELL**)malloc(sizeof(CELL*)*g.lifesrc_maxcells); 2217 | count = 0; 2218 | 2219 | for (gen = 0; gen < g.period; gen++) { 2220 | for (col = 1; col <= g.ncols; col++) { 2221 | for (row = 1; row <= g.nrows; row++) { 2222 | cell = findcell(row, col, gen); 2223 | // cells must be already loaded!!! 2224 | if ((cell->active) && (cell->state == UNK) && (!cell->unchecked)) 2225 | { 2226 | table[count++] = findcell(row, col, gen); 2227 | } 2228 | 2229 | } 2230 | } 2231 | } 2232 | 2233 | /* 2234 | * Now sort the table based on our desired search order. 2235 | */ 2236 | qsort((char *) table, count, sizeof(CELL *), ordersortfunc); 2237 | 2238 | /* 2239 | * Finally build the search list from the table elements in the 2240 | * final order. 2241 | */ 2242 | g.searchlist = NULL; 2243 | 2244 | while (--count >= 0) 2245 | { 2246 | cell = table[count]; 2247 | cell->search = g.searchlist; 2248 | g.searchlist = cell; 2249 | } 2250 | free(table); 2251 | } 2252 | 2253 | /* 2254 | * Set the state of a cell back to UNK/FREE 2255 | * Proceed through the loop if present 2256 | */ 2257 | 2258 | void 2259 | rescell(CELL *cell) 2260 | { 2261 | CELL *c1; 2262 | 2263 | if (cell->state == UNK) return; 2264 | 2265 | --g.cellcount; // take all loops as a single cell 2266 | 2267 | c1 = cell; 2268 | 2269 | if (cell->state == ON) { 2270 | // if it was previously ON, we have some more stats to hassle 2271 | do { 2272 | cell->state = UNK; 2273 | cell->free = TRUE; 2274 | if (cell->gen == 0) { // cannot move the test outwards due to looped frozen cells 2275 | --cell->rowinfo->oncount; 2276 | --cell->colinfo->oncount; 2277 | cell->colinfo->sumpos -= cell->row; 2278 | if (g.nearcols) adjustnear(cell, -1); 2279 | --g.g0oncellcount; 2280 | if (cell->colinfo->setcount == g.nrows) --g.fullcolumns; 2281 | --cell->colinfo->setcount; 2282 | } 2283 | 2284 | if (g.combining && (cell->combined != UNK)) 2285 | { 2286 | if (cell->combined == ON) 2287 | { 2288 | ++g.differentcombinedcells; 2289 | } 2290 | --g.setcombinedcells; 2291 | } 2292 | 2293 | cell = cell->loop; 2294 | } while (cell != c1); 2295 | 2296 | } else { 2297 | // OFF is a little easier to do 2298 | do { 2299 | cell->state = UNK; 2300 | cell->free = TRUE; 2301 | if (cell->gen == 0) { 2302 | if (cell->colinfo->setcount == g.nrows) --g.fullcolumns; 2303 | --cell->colinfo->setcount; 2304 | 2305 | } 2306 | 2307 | if (g.combining && (cell->combined != UNK)) 2308 | { 2309 | if (cell->combined == OFF) 2310 | { 2311 | ++g.differentcombinedcells; 2312 | } 2313 | --g.setcombinedcells; 2314 | } 2315 | 2316 | cell = cell->loop; 2317 | } while (cell != c1); 2318 | } 2319 | } 2320 | 2321 | /* 2322 | * Set the state of a cell to the specified state. 2323 | * The state is either ON or OFF. 2324 | * Returns ERROR if the setting is inconsistent. 2325 | * If the cell is newly set, then it is added to the set table. 2326 | */ 2327 | 2328 | BOOL 2329 | setcell(CELL *cell, STATE state, BOOL free) 2330 | { 2331 | CELL *c1; 2332 | if (cell->state == state) 2333 | { 2334 | DPRINTF4("setcell %d %d %d to state %s already set\n", 2335 | cell->row, cell->col, cell->gen, 2336 | (state == ON) ? "on" : "off"); 2337 | 2338 | return TRUE; 2339 | } 2340 | 2341 | if (cell->state != UNK) 2342 | { 2343 | DPRINTF4("setcell %d %d %d to state %s inconsistent\n", 2344 | cell->row, cell->col, cell->gen, 2345 | (state == ON) ? "on" : "off"); 2346 | 2347 | return FALSE; 2348 | } 2349 | 2350 | if (g.combining && (g.differentcombinedcells == 0)) return FALSE; 2351 | 2352 | c1 = cell; 2353 | 2354 | if (state == ON) { 2355 | // setting state ON 2356 | // first let's examine the stats 2357 | do { 2358 | if (cell->gen == 0) { 2359 | if ((g.usecol != 0) 2360 | && (g.colinfo[g.usecol].oncount == 0) 2361 | && (g.colinfo[g.usecol].setcount == g.nrows) && g.inited) 2362 | { 2363 | return FALSE; 2364 | } 2365 | 2366 | if ((g.maxcount != 0) && (g.g0oncellcount >= g.maxcount)) 2367 | { 2368 | return FALSE; 2369 | } 2370 | 2371 | if (g.nearcols && (cell->near1 <= 0) && (cell->col > 1) 2372 | && g.inited) 2373 | { 2374 | return FALSE; 2375 | } 2376 | 2377 | if (g.colcells && (cell->colinfo->oncount >= g.colcells) 2378 | && g.inited) 2379 | { 2380 | return FALSE; 2381 | } 2382 | 2383 | if (g.colwidth && g.inited && checkwidth(cell)) 2384 | return FALSE; 2385 | 2386 | if (g.nearcols) adjustnear(cell, 1); 2387 | 2388 | cell->rowinfo->oncount++; 2389 | 2390 | cell->colinfo->oncount++; 2391 | 2392 | cell->colinfo->setcount++; 2393 | 2394 | if (cell->colinfo->setcount == g.nrows) g.fullcolumns++; 2395 | 2396 | cell->colinfo->sumpos += cell->row; 2397 | 2398 | g.g0oncellcount++; 2399 | } 2400 | 2401 | cell->state = ON; 2402 | cell->free = free; 2403 | 2404 | if (cell->active) { 2405 | *g.newset++ = cell; 2406 | 2407 | *g.searchset++ = g.searchlist; 2408 | 2409 | while ((g.searchlist != NULL) && (g.searchlist->state != UNK)) { 2410 | g.searchlist = g.searchlist->search; 2411 | } 2412 | 2413 | free = FALSE; // all following cells in the loop are not free 2414 | 2415 | } 2416 | 2417 | if (g.combining &&(cell->combined != UNK)) 2418 | { 2419 | if (cell->combined == ON) 2420 | { 2421 | --g.differentcombinedcells; 2422 | } 2423 | ++g.setcombinedcells; 2424 | if ((g.setcombinedcells == g.combinedcells) && (g.differentcombinedcells == 0)) 2425 | { 2426 | return FALSE; 2427 | } 2428 | } 2429 | 2430 | cell = cell->loop; 2431 | } while (c1 != cell); 2432 | } else { 2433 | // setting state OFF is somewhat easier 2434 | do { 2435 | if (cell->gen == 0) { 2436 | if ((g.usecol != 0) 2437 | && (g.colinfo[g.usecol].oncount == 0) 2438 | && (g.colinfo[g.usecol].setcount == g.nrows) && g.inited) 2439 | { 2440 | return FALSE; 2441 | } 2442 | 2443 | cell->colinfo->setcount++; 2444 | 2445 | if (cell->colinfo->setcount == g.nrows) g.fullcolumns++; 2446 | } 2447 | 2448 | cell->state = OFF; 2449 | cell->free = free; 2450 | 2451 | if (cell->active) { 2452 | *g.newset++ = cell; 2453 | 2454 | *g.searchset++ = g.searchlist; 2455 | 2456 | while ((g.searchlist != NULL) && (g.searchlist->state != UNK)) { 2457 | g.searchlist = g.searchlist->search; 2458 | } 2459 | 2460 | free = FALSE; 2461 | } 2462 | 2463 | if (g.combining && (cell->combined != UNK)) 2464 | { 2465 | if (cell->combined == OFF) 2466 | { 2467 | --g.differentcombinedcells; 2468 | } 2469 | ++g.setcombinedcells; 2470 | if ((g.setcombinedcells == g.combinedcells) && (g.differentcombinedcells == 0)) 2471 | { 2472 | return FALSE; 2473 | } 2474 | } 2475 | 2476 | cell = cell->loop; 2477 | } while (c1 != cell); 2478 | } 2479 | 2480 | ++g.cellcount; // take whole loop as a single cell 2481 | 2482 | return TRUE; 2483 | } 2484 | 2485 | static __inline int 2486 | sumtodesc(STATE futurestate, STATE currentstate, int neighborsum) 2487 | { 2488 | // UNK = 0 2489 | // ON = 1 2490 | // OFF = 9 2491 | 2492 | // using the following expression, all different 2493 | // combinations are mapped to different numbers 2494 | // if you don't believe it, just try it 2495 | 2496 | return (neighborsum*10 + currentstate*3 + futurestate); 2497 | } 2498 | 2499 | /* 2500 | * Calculate the current descriptor for a cell. 2501 | */ 2502 | static __inline short 2503 | getdesc(CELL *cell) 2504 | { 2505 | return sumtodesc(cell->future->state, cell->state, 2506 | cell->cul->state + cell->cu->state + cell->cur->state 2507 | + cell->cdl->state + cell->cd->state + cell->cdr->state 2508 | + cell->cl->state + cell->cr->state); 2509 | } 2510 | 2511 | /* 2512 | * Consistify a cell. 2513 | * This means examine this cell in the previous generation, and 2514 | * make sure that the previous generation can validly produce the 2515 | * current cell. Returns FALSE if the cell is inconsistent. 2516 | */ 2517 | static BOOL consistify(CELL *cell) 2518 | { 2519 | CELL *prevcell; 2520 | CELL *neighbor; 2521 | int desc; 2522 | STATE state; 2523 | FLAGS flags; 2524 | 2525 | /* 2526 | * If we are searching for parents and this is generation 0, then 2527 | * the cell is consistent with respect to the previous generation. 2528 | */ 2529 | if (g.parent && (cell->gen == 0)) 2530 | return TRUE; 2531 | 2532 | // Now get the descriptor for the cell, its parent and its parent neighborhood 2533 | 2534 | prevcell = cell->past; 2535 | desc = getdesc(prevcell); 2536 | 2537 | // the implic table will tell us everything we need to know 2538 | 2539 | flags = g.implic[desc]; 2540 | 2541 | // first check if the state is consistent 2542 | 2543 | if (flags == IMPBAD) return FALSE; 2544 | 2545 | // the state is consistent 2546 | // now for the implications 2547 | 2548 | // change the cell if needed 2549 | if (((flags & IMPN) != 0) && 2550 | !setcell(cell, ((flags & IMPN1) != 0) ? ON : OFF, FALSE)) return FALSE; 2551 | 2552 | // change the parent cell if needed 2553 | if (((flags & IMPC) != 0) && 2554 | !setcell(prevcell, ((flags & IMPC1) != 0) ? ON : OFF, FALSE)) return FALSE; 2555 | 2556 | if ((flags & IMPUN) != 0) { 2557 | // let's change the parent neighborhood 2558 | state = ((flags & IMPUN1) != 0) ? ON : OFF; 2559 | 2560 | neighbor = prevcell->cul; 2561 | if ((neighbor->state == UNK) && 2562 | !setcell(neighbor, state, FALSE)) return FALSE; 2563 | 2564 | neighbor = prevcell->cu; 2565 | if ((neighbor->state == UNK) && 2566 | !setcell(neighbor, state, FALSE)) return FALSE; 2567 | 2568 | neighbor = prevcell->cur; 2569 | if ((neighbor->state == UNK) && 2570 | !setcell(neighbor, state, FALSE)) return FALSE; 2571 | 2572 | neighbor = prevcell->cr; 2573 | if ((neighbor->state == UNK) && 2574 | !setcell(neighbor, state, FALSE)) return FALSE; 2575 | 2576 | neighbor = prevcell->cdr; 2577 | if ((neighbor->state == UNK) && 2578 | !setcell(neighbor, state, FALSE)) return FALSE; 2579 | 2580 | neighbor = prevcell->cd; 2581 | if ((neighbor->state == UNK) && 2582 | !setcell(neighbor, state, FALSE)) return FALSE; 2583 | 2584 | neighbor = prevcell->cdl; 2585 | if ((neighbor->state == UNK) && 2586 | !setcell(neighbor, state, FALSE)) return FALSE; 2587 | 2588 | neighbor = prevcell->cl; 2589 | if ((neighbor->state == UNK) && 2590 | !setcell(neighbor, state, FALSE)) return FALSE; 2591 | } 2592 | 2593 | DPRINTF0("Implications successful\n"); 2594 | 2595 | return TRUE; 2596 | } 2597 | 2598 | 2599 | /* 2600 | * See if a cell and its neighbors are consistent with the cell and its 2601 | * neighbors in the next generation. 2602 | */ 2603 | static BOOL 2604 | consistify10(CELL *cell) 2605 | { 2606 | if (!consistify(cell)) 2607 | return FALSE; 2608 | 2609 | cell = cell->future; 2610 | 2611 | return consistify(cell) 2612 | && consistify(cell->cul) 2613 | && consistify(cell->cu) 2614 | && consistify(cell->cur) 2615 | && consistify(cell->cl) 2616 | && consistify(cell->cr) 2617 | && consistify(cell->cdl) 2618 | && consistify(cell->cd) 2619 | && consistify(cell->cdr); 2620 | } 2621 | 2622 | 2623 | /* 2624 | * Examine the next choice of cell settings. 2625 | */ 2626 | STATUS 2627 | examinenext(void) 2628 | { 2629 | CELL * cell; 2630 | 2631 | /* 2632 | * If there are no more cells to examine, then what we have 2633 | * is consistent. 2634 | */ 2635 | if (g.nextset == g.newset) 2636 | return CONSISTENT; 2637 | 2638 | /* 2639 | * Get the next cell to examine, and check it out for symmetry 2640 | * and for consistency with its previous and next generations. 2641 | */ 2642 | cell = *g.nextset++; 2643 | 2644 | DPRINTF4("Examining saved cell %d %d %d (%s) for consistency\n", 2645 | cell->row, cell->col, cell->gen, 2646 | (cell->free ? "free" : "forced")); 2647 | 2648 | return consistify10(cell) ? OK : ERROR1; 2649 | } 2650 | 2651 | 2652 | /* 2653 | * Set a cell to the specified value and determine all consequences we 2654 | * can from the choice. Consequences are a contradiction or a consistency. 2655 | */ 2656 | BOOL 2657 | proceed(cell, state, free) 2658 | CELL * cell; 2659 | STATE state; 2660 | BOOL free; 2661 | { 2662 | int status; 2663 | 2664 | if (!setcell(cell, state, free)) 2665 | return FALSE; 2666 | 2667 | do { 2668 | status = examinenext(); 2669 | } while (status == OK); 2670 | 2671 | return (status == CONSISTENT); 2672 | } 2673 | 2674 | 2675 | /* 2676 | * Back up the list of set cells to undo choices. 2677 | * Returns the cell which is to be tried for the other possibility. 2678 | * Returns NULL on an "object cannot exist" error. 2679 | */ 2680 | CELL * 2681 | backup(void) 2682 | { 2683 | CELL * cell; 2684 | 2685 | // first let's find how far to backup 2686 | 2687 | g.nextset = g.newset; 2688 | 2689 | while (g.nextset != g.settable) 2690 | { 2691 | cell = *--g.nextset; 2692 | --g.searchset; 2693 | 2694 | if (!cell->free) continue; 2695 | 2696 | // free cell found 2697 | // record old status 2698 | g.prevstate = cell->state; 2699 | 2700 | g.searchlist = *g.searchset; 2701 | 2702 | // reset the stack and return the cell 2703 | while (g.newset != g.nextset) { 2704 | rescell(*--g.newset); 2705 | } 2706 | 2707 | return cell; 2708 | } 2709 | 2710 | // free cell not found 2711 | // let's reset the stack anyway 2712 | 2713 | while (g.newset != g.nextset) { 2714 | rescell(*--g.newset); 2715 | } 2716 | 2717 | return NULL; 2718 | } 2719 | 2720 | 2721 | /* 2722 | * Do checking based on setting the specified cell. 2723 | * Returns ERROR if an inconsistency was found. 2724 | */ 2725 | BOOL 2726 | go(CELL *cell, STATE state, BOOL free) 2727 | { 2728 | CELL ** setpos; 2729 | 2730 | for (;;) 2731 | { 2732 | 2733 | setpos = g.nextset; 2734 | 2735 | if (proceed(cell, state, free)) return TRUE; 2736 | 2737 | if ((setpos == g.nextset) && free) 2738 | { 2739 | // no cell added to stack 2740 | // no backup required 2741 | // but prevstate is not defined now 2742 | state = (ON + OFF) - state; 2743 | } else { 2744 | cell = backup(); 2745 | 2746 | if (cell == NULL) return FALSE; 2747 | 2748 | state = (ON + OFF) - g.prevstate; 2749 | } 2750 | free = FALSE; 2751 | } 2752 | } 2753 | 2754 | 2755 | /* 2756 | * Find another unknown cell in a normal search. 2757 | * Returns NULL if there are no more unknown cells. 2758 | */ 2759 | static CELL * 2760 | getnormalunknown(void) 2761 | { 2762 | CELL * cell; 2763 | 2764 | for (cell = g.searchlist; cell != NULL; cell = cell->search) 2765 | { 2766 | if (cell->state == UNK) 2767 | { 2768 | g.searchlist = cell; 2769 | 2770 | return cell; 2771 | } 2772 | } 2773 | 2774 | return NULL; 2775 | } 2776 | 2777 | /* 2778 | * Find another unknown cell when averaging is done. 2779 | * Returns NULL if there are no more unknown cells. 2780 | */ 2781 | 2782 | static CELL * 2783 | getaverageunknown(void) 2784 | { 2785 | CELL * cell; 2786 | CELL * bestcell; 2787 | int bestdist; 2788 | int curdist; 2789 | int wantrow; 2790 | int curcol; 2791 | int testcol; 2792 | 2793 | bestcell = NULL; 2794 | bestdist = -1; 2795 | 2796 | cell = g.searchlist; 2797 | 2798 | while (cell) 2799 | { 2800 | g.searchlist = cell; 2801 | curcol = cell->col; 2802 | 2803 | testcol = curcol - 1; 2804 | 2805 | while ((testcol > 0) && (g.colinfo[testcol].oncount <= 0)) 2806 | testcol--; 2807 | 2808 | if (testcol > 0) 2809 | { 2810 | wantrow = g.colinfo[testcol].sumpos / 2811 | g.colinfo[testcol].oncount; 2812 | } 2813 | else 2814 | wantrow = (g.nrows + 1) / 2; 2815 | 2816 | for (; (cell != NULL) && (cell->col == curcol); cell = cell->search) 2817 | { 2818 | if (cell->state == UNK) 2819 | { 2820 | curdist = cell->row - wantrow; 2821 | 2822 | if (curdist < 0) 2823 | curdist = -curdist; 2824 | 2825 | if (curdist > bestdist) 2826 | { 2827 | bestcell = cell; 2828 | bestdist = curdist; 2829 | } 2830 | } 2831 | } 2832 | 2833 | if (bestcell) 2834 | return bestcell; 2835 | } 2836 | 2837 | return NULL; 2838 | } 2839 | 2840 | // calculate how many cells will change 2841 | // if we change the current cell to ON or OFF 2842 | // set smartlen1 and smartlen0 to appropriate numbers 2843 | 2844 | static BOOL getsmartnumbers(CELL *cell) 2845 | { 2846 | int cellno; 2847 | int comb0, comb1; 2848 | CELL ** setpos; 2849 | 2850 | // known and inactive cells are unimportant 2851 | if (cell->state != UNK) return 2; 2852 | 2853 | // remember set position for proper backup 2854 | setpos = g.newset; 2855 | 2856 | // remember cell count to calculate the change 2857 | cellno = g.cellcount; 2858 | 2859 | comb0 = comb1 = g.differentcombinedcells + g.setcombinedcells; 2860 | // test the cell 2861 | if (proceed(cell, ON, TRUE)) 2862 | { 2863 | g.smartlen1 = g.cellcount - cellno; 2864 | comb1 = g.differentcombinedcells + g.setcombinedcells - comb1; 2865 | 2866 | // back up 2867 | backup(); 2868 | 2869 | // and now let's try the OFF choice 2870 | 2871 | if (proceed(cell, OFF, TRUE)) 2872 | { 2873 | g.smartlen0 = g.cellcount - cellno; 2874 | comb0 = g.differentcombinedcells + g.setcombinedcells - comb0; 2875 | 2876 | if (g.smarton) 2877 | { 2878 | if (comb0 == comb1) 2879 | { 2880 | g.smartcomb = comb0; 2881 | g.smartchoice = (g.smartlen1 > g.smartlen0) ? ON : OFF; 2882 | } 2883 | else if (comb0 > comb1) 2884 | { 2885 | g.smartcomb = comb0; 2886 | g.smartchoice = OFF; 2887 | } 2888 | else 2889 | { 2890 | g.smartcomb = comb1; 2891 | g.smartchoice = ON; 2892 | } 2893 | } 2894 | else 2895 | { 2896 | g.smartcomb = 0; 2897 | g.smartchoice = (g.smartlen1 > g.smartlen0) ? ON : OFF; 2898 | } 2899 | 2900 | // back up 2901 | backup(); 2902 | 2903 | return TRUE; 2904 | 2905 | } else { 2906 | // OFF state inconsistent 2907 | // makes a good candidate 2908 | g.smartchoice = OFF; 2909 | 2910 | // back up if something changed 2911 | if (setpos != g.newset) backup(); 2912 | 2913 | return FALSE; 2914 | } 2915 | 2916 | } else { 2917 | // ON state inconsistent 2918 | // it's actually a good candidate 2919 | g.smartchoice = ON; 2920 | 2921 | // back up if something changed 2922 | if (setpos != g.newset) backup(); 2923 | 2924 | return FALSE; 2925 | 2926 | } 2927 | } 2928 | 2929 | // Smart cell ordering 2930 | 2931 | static CELL * 2932 | getsmartunknown(void) 2933 | { 2934 | CELL *cell; 2935 | CELL *best; 2936 | STATE bestchoice; 2937 | int max, window, threshold, bestlen1, bestlen0, bestcomb, wnd, n1, n2, a, b, c, d; 2938 | 2939 | // Move the searchlist over all known cells 2940 | while ((g.searchlist != NULL) && (g.searchlist->state != UNK)) { 2941 | g.searchlist = g.searchlist->search; 2942 | } 2943 | 2944 | // Return NULL if no unknown cells 2945 | if (g.searchlist == NULL) return NULL; 2946 | 2947 | // Prepare threshold 2948 | threshold = g.smartthreshold; 2949 | if (threshold <= 0) threshold = g.lifesrc_maxcells; 2950 | 2951 | // Prepare the dummy maximum 2952 | max = 2; // at least 3 cells must change 2953 | bestlen0 = 1; 2954 | bestlen1 = 1; 2955 | bestcomb = 0; 2956 | 2957 | best = NULL; 2958 | 2959 | wnd = 0; 2960 | 2961 | window = g.smartwindow; 2962 | cell = g.searchlist; 2963 | 2964 | while ((cell != NULL) && (window > 0) && (max < threshold)) { 2965 | ++wnd; 2966 | --window; // count known cells too 2967 | if (cell->state == UNK) 2968 | { 2969 | if (getsmartnumbers(cell)) 2970 | { 2971 | if (bestcomb == g.smartcomb) 2972 | { 2973 | // (smartlen0, smartlen1) is better than (bestlen0, bestlen1) if 2974 | // 1/2**smartlen0 + 1/2**smartlen1 < 1/2**bestlen0 + 1/2**bestlen1 2975 | // i.e. 2976 | // 2**(b0+b1+s0) + 2**(b0+b1+s1) < 2**(s0+s1+b0) + 2**(s0+s1+b1) 2977 | // 2978 | // both sides are binary numbers with one or two 1's 2979 | // so let's compare the exponents 2980 | 2981 | n1 = g.smartlen0 + g.smartlen1; 2982 | n2 = n1 + bestlen1; 2983 | n1 += bestlen0; 2984 | 2985 | if (n1 > n2) 2986 | { 2987 | a = n1; 2988 | b = n2; 2989 | } else if (n1 == n2) { 2990 | a = n1 + 1; 2991 | b = -1; 2992 | } else { 2993 | a = n2; 2994 | b = n1; 2995 | } 2996 | 2997 | // max = bestlen0 + bestlen1 2998 | 2999 | n1 = max + g.smartlen0; 3000 | n2 = max + g.smartlen1; 3001 | 3002 | if (n1 > n2) 3003 | { 3004 | c = n1; 3005 | d = n2; 3006 | } else if (n1 == n2) { 3007 | c = n1 + 1; 3008 | d = -1; 3009 | } else { 3010 | c = n2; 3011 | d = n1; 3012 | } 3013 | 3014 | if ((a > c) || ((a = c) && (b > d))) 3015 | { 3016 | best = cell; 3017 | bestchoice = g.smartchoice; 3018 | // bestcomb = g.smartcomb; -- it's equal anyway 3019 | bestlen1 = g.smartlen1; 3020 | bestlen0 = g.smartlen0; 3021 | max = bestlen0 + bestlen1; 3022 | } 3023 | } 3024 | else if (bestcomb < g.smartcomb) 3025 | { 3026 | best = cell; 3027 | bestchoice = g.smartchoice; 3028 | bestcomb = g.smartcomb; 3029 | bestlen1 = g.smartlen1; 3030 | bestlen0 = g.smartlen0; 3031 | max = bestlen0 + bestlen1; 3032 | } 3033 | } else { 3034 | // the cell can be set only one way 3035 | best = cell; 3036 | bestchoice = g.smartchoice; 3037 | max = g.lifesrc_maxcells + 1; 3038 | window = 0; 3039 | } 3040 | } 3041 | cell = cell->search; 3042 | } 3043 | 3044 | // Found something? 3045 | if (best != NULL) { 3046 | 3047 | 3048 | if (g.lifesrc_maxcells >= max) { 3049 | g.smartstatsumwnd += wnd; 3050 | ++g.smartstatsumwndc; 3051 | g.smartstatsumlen += max; 3052 | ++g.smartstatsumlenc; 3053 | 3054 | if (g.smartstatsumwndc >= 100000) { 3055 | g.smartstatwnd = g.smartstatsumwnd / g.smartstatsumwndc; 3056 | g.smartstatsumwnd = 0; 3057 | g.smartstatsumwndc = 0; 3058 | if (g.smartstatsumlenc >= 10000) { 3059 | g.smartstatlen = g.smartstatsumlen / g.smartstatsumlenc; 3060 | g.smartstatsumlen = 0; 3061 | g.smartstatsumlenc = 0; 3062 | } 3063 | } 3064 | } 3065 | 3066 | if (g.smarton) 3067 | { 3068 | // propose the shorter tree 3069 | g.smartchoice = bestchoice; 3070 | } else { 3071 | g.smartchoice = UNK; 3072 | } 3073 | return best; 3074 | } 3075 | 3076 | // fall back to standard 3077 | g.smartchoice = UNK; 3078 | 3079 | // Just return the first UNK cell 3080 | // This shouldn't be often anyway 3081 | 3082 | return g.searchlist; 3083 | } 3084 | 3085 | /* 3086 | * Choose a state for an unknown cell, either OFF or ON. 3087 | * Normally, we try to choose OFF cells first to terminate an object. 3088 | * But for follow generations mode, we try to choose the same setting 3089 | * as a nearby generation. 3090 | */ 3091 | 3092 | static STATE 3093 | choose(cell) 3094 | CELL * cell; 3095 | { 3096 | /* 3097 | * if something pre-set by the select algorithm, 3098 | * use the selection 3099 | */ 3100 | 3101 | if (g.smartchoice != UNK) return g.smartchoice; 3102 | 3103 | /* 3104 | * If we are following cells in other generations, 3105 | * then try to do that. 3106 | */ 3107 | 3108 | if (g.followgens) 3109 | { 3110 | if ((cell->past->state == ON) || 3111 | (cell->future->state == ON)) 3112 | { 3113 | return ON; 3114 | } 3115 | 3116 | } 3117 | 3118 | /* 3119 | * In all other cases 3120 | * try the OFF state first 3121 | */ 3122 | 3123 | return OFF; 3124 | } 3125 | 3126 | CELL *combinebackup(void); 3127 | 3128 | /* 3129 | * The top level search routine. 3130 | * Returns if an object is found, or is impossible. 3131 | */ 3132 | STATUS 3133 | search(void) 3134 | { 3135 | CELL * cell; 3136 | BOOL free; 3137 | BOOL needwrite; 3138 | STATE state; 3139 | 3140 | cell = (*getunknown)(); 3141 | 3142 | if (cell == NULL) 3143 | { 3144 | // nothing to search 3145 | // so we are at a solution 3146 | // let's start search for another one 3147 | 3148 | cell = backup(); 3149 | 3150 | if (cell == NULL) 3151 | return ERROR1; 3152 | 3153 | free = FALSE; 3154 | state = (ON + OFF) - g.prevstate; 3155 | 3156 | } else { 3157 | 3158 | state = choose(cell); 3159 | free = TRUE; 3160 | 3161 | } 3162 | 3163 | for (;;) { 3164 | if(abortthread) 3165 | { 3166 | return OK; 3167 | } 3168 | 3169 | // Set the state of the new cell. 3170 | 3171 | if (!go(cell, state, free)) 3172 | { 3173 | wlsUpdateProgressCounter(); 3174 | wlsUpdateAndShowTmpField_Sync(); 3175 | 3176 | return NOTEXIST; 3177 | } 3178 | 3179 | 3180 | // If it is time to dump our state, then do that. 3181 | 3182 | if (g.dumpfreq && (++g.dumpcount >= g.dumpfreq)) 3183 | { 3184 | g.dumpcount = 0; 3185 | dumpstate(NULL, g.dumpfile, FALSE); 3186 | } 3187 | 3188 | 3189 | // If we have enough columns found, then remember to 3190 | // write it to the output file. Also keep the last 3191 | // columns count values up to date. 3192 | 3193 | needwrite = FALSE; 3194 | 3195 | if (g.outputcols && 3196 | (g.fullcolumns >= g.outputlastcols + g.outputcols)) 3197 | { 3198 | g.outputlastcols = g.fullcolumns; 3199 | needwrite = TRUE; 3200 | } 3201 | 3202 | if (g.outputlastcols > g.fullcolumns) 3203 | g.outputlastcols = g.fullcolumns; 3204 | 3205 | // If it is time to view the progress,then show it. 3206 | 3207 | if (needwrite || (g.viewfreq && (++g.viewcount >= g.viewfreq))) 3208 | { 3209 | wlsUpdateProgressCounter(); 3210 | wlsUpdateAndShowTmpField_Sync(); 3211 | } 3212 | 3213 | // Write the progress to the output file if needed. 3214 | // This is done after viewing it so that the write 3215 | // message will stay visible for a while. 3216 | 3217 | if (needwrite) 3218 | { 3219 | wlsWriteCurrentFieldToFile(NULL, g.outputfile, TRUE); 3220 | } 3221 | 3222 | 3223 | // Get the next unknown cell and choose its state. 3224 | 3225 | cell = (*getunknown)(); 3226 | 3227 | if (cell == NULL) 3228 | return FOUND; 3229 | 3230 | if (g.stoponstep) { 3231 | wlsUpdateProgressCounter(); 3232 | wlsUpdateAndShowTmpField_Sync(); 3233 | abortthread = 1; 3234 | return OK; 3235 | } 3236 | 3237 | state = choose(cell); 3238 | free = TRUE; 3239 | } 3240 | } 3241 | 3242 | 3243 | /* 3244 | * Increment or decrement the near count in all the cells affected by 3245 | * this cell. This is done for all cells in the next columns which are 3246 | * within the distance specified the nearcols value. In this way, a 3247 | * quick test can be made to see if a cell is within range of another one. 3248 | */ 3249 | 3250 | void 3251 | adjustnear(CELL *cell, int inc) 3252 | { 3253 | CELL * curcell; 3254 | int count; 3255 | int colcount; 3256 | 3257 | for (colcount = g.nearcols; colcount > 0; colcount--) 3258 | { 3259 | cell = cell->cr; 3260 | curcell = cell; 3261 | 3262 | for (count = g.nearcols; count-- >= 0; curcell = curcell->cu) 3263 | curcell->near1 += inc; 3264 | 3265 | curcell = cell->cd; 3266 | 3267 | for (count = g.nearcols; count-- > 0; curcell = curcell->cd) 3268 | curcell->near1 += inc; 3269 | } 3270 | } 3271 | 3272 | 3273 | /* 3274 | * Check to see if setting the specified cell ON would make the width of 3275 | * the column exceed the allowed value. For symmetric objects, the width 3276 | * is only measured from the center to an edge. Returns TRUE if the cell 3277 | * would exceed the value. 3278 | */ 3279 | 3280 | static BOOL 3281 | checkwidth(cell) 3282 | CELL * cell; 3283 | { 3284 | int left; 3285 | int width; 3286 | int minrow; 3287 | int maxrow; 3288 | int srcminrow; 3289 | int srcmaxrow; 3290 | CELL * ucp; 3291 | CELL * dcp; 3292 | BOOL full; 3293 | 3294 | if (!g.colwidth || !g.inited || cell->gen) 3295 | return FALSE; 3296 | 3297 | left = cell->colinfo->oncount; 3298 | 3299 | if (left <= 0) 3300 | return FALSE; 3301 | 3302 | ucp = cell; 3303 | dcp = cell; 3304 | width = g.colwidth; 3305 | minrow = cell->row; 3306 | maxrow = cell->row; 3307 | srcminrow = 1; 3308 | srcmaxrow = g.nrows; 3309 | full = TRUE; 3310 | 3311 | if ((g.rowsym && (cell->col >= g.rowsym)) || 3312 | (g.fliprows && (cell->col >= g.fliprows))) 3313 | { 3314 | full = FALSE; 3315 | srcmaxrow = (g.nrows + 1) / 2; 3316 | 3317 | if (cell->row > srcmaxrow) 3318 | { 3319 | srcminrow = (g.nrows / 2) + 1; 3320 | srcmaxrow = g.nrows; 3321 | } 3322 | } 3323 | 3324 | while (left > 0) 3325 | { 3326 | if (full && (--width <= 0)) 3327 | return TRUE; 3328 | 3329 | ucp = ucp->cu; 3330 | dcp = dcp->cd; 3331 | 3332 | if (ucp->state == ON) 3333 | { 3334 | if (ucp->row >= srcminrow) 3335 | minrow = ucp->row; 3336 | 3337 | left--; 3338 | } 3339 | 3340 | if (dcp->state == ON) 3341 | { 3342 | if (dcp->row <= srcmaxrow) 3343 | maxrow = dcp->row; 3344 | 3345 | left--; 3346 | } 3347 | } 3348 | 3349 | if (maxrow - minrow >= g.colwidth) 3350 | return TRUE; 3351 | 3352 | return FALSE; 3353 | } 3354 | 3355 | 3356 | /* 3357 | * Check to see if any other generation is identical to generation 0. 3358 | * This is used to detect and weed out all objects with subperiods. 3359 | * (For example, stable objects or period 2 objects when using -g4.) 3360 | * Returns TRUE if there is an identical generation. 3361 | */ 3362 | BOOL 3363 | subperiods(void) 3364 | { 3365 | int row; 3366 | int col; 3367 | int gen; 3368 | CELL * cellg0; 3369 | CELL * cellgn; 3370 | 3371 | for (gen = 1; gen < g.period; gen++) 3372 | { 3373 | if (g.period % gen) 3374 | continue; 3375 | 3376 | for (row = 1; row <= g.nrows; row++) 3377 | { 3378 | for (col = 1; col <= g.ncols; col++) 3379 | { 3380 | cellg0 = findcell(row, col, 0); 3381 | cellgn = findcell(row, col, gen); 3382 | 3383 | if (cellg0->state != cellgn->state) 3384 | goto nextgen; 3385 | } 3386 | } 3387 | 3388 | return TRUE; 3389 | nextgen:; 3390 | } 3391 | 3392 | return FALSE; 3393 | } 3394 | 3395 | 3396 | /* 3397 | * Return the mapping of a cell from the last generation back to the first 3398 | * generation, or vice versa. This implements all flipping and translating 3399 | * of cells between these two generations. This routine should only be 3400 | * called for cells belonging to those two generations. 3401 | */ 3402 | static CELL * 3403 | mapcell(cell) 3404 | CELL * cell; 3405 | { 3406 | int row; 3407 | int col; 3408 | int tmp; 3409 | BOOL forward; 3410 | 3411 | row = cell->row; 3412 | col = cell->col; 3413 | forward = (cell->gen != 0); 3414 | 3415 | if (g.fliprows && (col >= g.fliprows)) 3416 | row = g.nrows + 1 - row; 3417 | 3418 | if (g.flipcols && (row >= g.flipcols)) 3419 | col = g.ncols + 1 - col; 3420 | 3421 | if (g.flipquads) 3422 | { /* NEED TO GO BACKWARDS */ 3423 | tmp = col; 3424 | col = row; 3425 | row = g.ncols + 1 - tmp; 3426 | } 3427 | 3428 | if (forward) 3429 | { 3430 | row += g.rowtrans; 3431 | col += g.coltrans; 3432 | } 3433 | else 3434 | { 3435 | row -= g.rowtrans; 3436 | col -= g.coltrans; 3437 | } 3438 | 3439 | if (forward) 3440 | return findcell(row, col, 0); 3441 | else 3442 | return findcell(row, col, g.period - 1); 3443 | } 3444 | 3445 | 3446 | /* 3447 | * Make the two specified cells belong to the same loop. 3448 | * If the two cells already belong to loops, the loops are joined. 3449 | * This will force the state of these two cells to follow each other. 3450 | * Symmetry uses this feature, and so does setting stable cells. 3451 | * If any cells in the loop are frozen, then they all are. 3452 | */ 3453 | BOOL loopcells(CELL *cell1, CELL *cell2) 3454 | { 3455 | CELL * cell; 3456 | BOOL frozen; 3457 | 3458 | if (cell2==NULL) return TRUE; 3459 | 3460 | if (cell1 == cell2) return TRUE; 3461 | 3462 | /* 3463 | * See if the second cell is already part of the first cell's loop. 3464 | * If so, they they are already joined. We don't need to 3465 | * check the other direction. 3466 | */ 3467 | for (cell = cell1->loop; cell != cell1; cell = cell->loop) 3468 | { 3469 | if (cell == cell2) return TRUE; 3470 | } 3471 | 3472 | /* 3473 | * The two cells belong to separate loops. 3474 | * Break each of those loops and make one big loop from them. 3475 | */ 3476 | cell = cell1->loop; 3477 | cell1->loop = cell2->loop; 3478 | cell2->loop = cell; 3479 | 3480 | /* 3481 | * See if any of the cells in the loop are frozen. 3482 | * If so, then mark all of the cells in the loop frozen 3483 | * since they effectively are anyway. This lets the 3484 | * user see that fact. 3485 | */ 3486 | frozen = cell1->frozen; 3487 | 3488 | for (cell = cell1->loop; cell != cell1; cell = cell->loop) 3489 | { 3490 | if (cell->frozen) 3491 | frozen = TRUE; 3492 | } 3493 | 3494 | if (frozen) 3495 | { 3496 | cell1->frozen = TRUE; 3497 | 3498 | for (cell = cell1->loop; cell != cell1; cell = cell->loop) 3499 | cell->frozen = TRUE; 3500 | } 3501 | 3502 | return TRUE; 3503 | } 3504 | 3505 | 3506 | /* 3507 | * Return a cell which is symmetric to the given cell. 3508 | * It is not necessary to know all symmetric cells to a single cell, 3509 | * as long as all symmetric cells are chained in a loop. Thus a single 3510 | * pointer is good enough even for the case of both row and column symmetry. 3511 | * Returns NULL if there is no symmetry. 3512 | */ 3513 | 3514 | 3515 | static CELL *symcell(CELL *cell) 3516 | { 3517 | int row; 3518 | int col; 3519 | int nrow; 3520 | int ncol; 3521 | 3522 | if(!g.symmetry) 3523 | return NULL; 3524 | 3525 | row = cell->row; 3526 | col = cell->col; 3527 | nrow = g.nrows + 1 - row; 3528 | ncol = g.ncols + 1 - col; 3529 | 3530 | if(g.symmetry==1) // col sym 3531 | return findcell(row,ncol,cell->gen); 3532 | 3533 | if(g.symmetry==2) { // row sym 3534 | return findcell(nrow,col,cell->gen); 3535 | } 3536 | 3537 | if(g.symmetry==3) // fwd diag 3538 | return findcell(ncol,nrow,cell->gen); 3539 | 3540 | if(g.symmetry==4) { // bwd diag 3541 | return findcell(col,row,cell->gen); 3542 | } 3543 | 3544 | if(g.symmetry==5) // origin 3545 | return findcell(nrow, ncol, cell->gen); 3546 | 3547 | if(g.symmetry==6) { 3548 | /* 3549 | * Here is there is both row and column symmetry. 3550 | * First see if the cell is in the middle row or middle column, 3551 | * and if so, then this is easy. 3552 | */ 3553 | if ((nrow == row) || (ncol == col)) 3554 | return findcell(nrow, ncol, cell->gen); 3555 | 3556 | /* 3557 | * The cell is really in one of the four quadrants, and therefore 3558 | * has four cells making up the symmetry. Link this cell to the 3559 | * symmetrical cell in the next quadrant clockwise. 3560 | */ 3561 | if ((row < nrow) == (col < ncol)) 3562 | return findcell(row, ncol, cell->gen); // quadrant 2 or 4 3563 | else 3564 | return findcell(nrow, col, cell->gen); // quadrant 1 or 3 3565 | } 3566 | 3567 | if(g.symmetry==7) { // diagonal 4-fold 3568 | // if on a diagonal... 3569 | if(row==col || row==ncol) 3570 | return findcell(nrow,ncol,cell->gen); 3571 | 3572 | // Not on a diagonal. 3573 | if((colgen); 3575 | else 3576 | return findcell(ncol,nrow,cell->gen); 3577 | } 3578 | 3579 | if(g.symmetry==8) { // origin*4 symmetry 3580 | // this is surprisingly simple 3581 | return findcell(ncol,row,cell->gen); 3582 | } 3583 | 3584 | if(g.symmetry==9) { // octagonal, this is gonna be tough 3585 | // if on an axis 3586 | if(nrow==row || ncol==col) 3587 | return findcell(ncol,row,cell->gen); 3588 | // if on a diagonal 3589 | if(row==col || row==ncol) 3590 | return findcell(ncol,row,cell->gen); 3591 | if((col>nrow && rownrow)) // octants 1,5 3592 | return findcell(nrow,col,cell->gen); // flip rows 3593 | if((colncol)||(col>nrow && colgen); // fwd diag 3595 | if((col>row && colncol)) // 3,7 3596 | return findcell(row,ncol,cell->gen); // flip cols 3597 | if((colrow && row>nrow)) // 4,8 3598 | return findcell(col,row,cell->gen); // bwd diag 3599 | 3600 | } 3601 | 3602 | return NULL; // crash if we get here :) 3603 | } 3604 | 3605 | /* 3606 | * Link a cell to its eight neighbors in the same generation, and also 3607 | * link those neighbors back to this cell. 3608 | */ 3609 | static void 3610 | linkcell(cell) 3611 | CELL * cell; 3612 | { 3613 | int row; 3614 | int col; 3615 | int gen; 3616 | CELL * paircell; 3617 | 3618 | row = cell->row; 3619 | col = cell->col; 3620 | gen = cell->gen; 3621 | 3622 | paircell = findcell(row - 1, col - 1, gen); 3623 | cell->cul = paircell; 3624 | paircell->cdr = cell; 3625 | 3626 | paircell = findcell(row - 1, col, gen); 3627 | cell->cu = paircell; 3628 | paircell->cd = cell; 3629 | 3630 | paircell = findcell(row - 1, col + 1, gen); 3631 | cell->cur = paircell; 3632 | paircell->cdl = cell; 3633 | 3634 | paircell = findcell(row, col - 1, gen); 3635 | cell->cl = paircell; 3636 | paircell->cr = cell; 3637 | 3638 | paircell = findcell(row, col + 1, gen); 3639 | cell->cr = paircell; 3640 | paircell->cl = cell; 3641 | 3642 | paircell = findcell(row + 1, col - 1, gen); 3643 | cell->cdl = paircell; 3644 | paircell->cur = cell; 3645 | 3646 | paircell = findcell(row + 1, col, gen); 3647 | cell->cd = paircell; 3648 | paircell->cu = cell; 3649 | 3650 | paircell = findcell(row + 1, col + 1, gen); 3651 | cell->cdr = paircell; 3652 | paircell->cul = cell; 3653 | } 3654 | 3655 | 3656 | /* 3657 | * Find a cell given its coordinates. 3658 | * Most coordinates range from 0 to colmax+1, 0 to rowmax+1, and 0 to genmax-1. 3659 | * Cells within this range are quickly found by indexing into celltable. 3660 | * Cells outside of this range are handled by searching an auxillary table, 3661 | * and are dynamically created as necessary. 3662 | */ 3663 | CELL * 3664 | findcell(row, col, gen) 3665 | int row; 3666 | int col; 3667 | int gen; 3668 | { 3669 | CELL * cell; 3670 | int i; 3671 | 3672 | /* 3673 | * If the cell is a normal cell, then we know where it is. 3674 | */ 3675 | if ((row >= 0) && (row <= g.nrows + 1) && 3676 | (col >= 0) && (col <= g.ncols + 1) && 3677 | (gen >= 0) && (gen < g.period)) 3678 | { 3679 | return g.celltable[(col * (g.nrows + 2) + row) * g.period + gen]; 3680 | } 3681 | 3682 | /* 3683 | * See if the cell is already allocated in the auxillary table. 3684 | */ 3685 | for (i = 0; i < g.auxcellcount; i++) 3686 | { 3687 | cell = g.auxtable[i]; 3688 | 3689 | if ((cell->row == row) && (cell->col == col) && 3690 | (cell->gen == gen)) 3691 | { 3692 | return cell; 3693 | } 3694 | } 3695 | 3696 | /* 3697 | * Need to allocate the cell and add it to the auxillary table. 3698 | */ 3699 | cell = allocatecell(); 3700 | cell->row = row; 3701 | cell->col = col; 3702 | cell->gen = gen; 3703 | cell->rowinfo = &g.dummyrowinfo; 3704 | cell->colinfo = &g.dummycolinfo; 3705 | 3706 | if(g.auxcellcount>=g.auxtable_alloc) { 3707 | // Ran out of space in the aux cell table. 3708 | // (Not sure this is necessary in the "K" configuration.) 3709 | g.auxtable_alloc *= 2; 3710 | g.auxtable = (CELL**)realloc(g.auxtable,sizeof(CELL*)*g.auxtable_alloc); 3711 | } 3712 | g.auxtable[g.auxcellcount++] = cell; 3713 | 3714 | return cell; 3715 | } 3716 | 3717 | 3718 | /* 3719 | * Allocate a new cell. 3720 | * The cell is initialized as if it was a boundary cell. 3721 | */ 3722 | static CELL * 3723 | allocatecell(void) 3724 | { 3725 | CELL * cell; 3726 | 3727 | /* 3728 | * Allocate a new chunk of cells if there are none left. 3729 | */ 3730 | if (g.newcellcount <= 0) 3731 | { 3732 | g.newcells = (CELL *) malloc(sizeof(CELL) * ALLOCSIZE); 3733 | 3734 | if (g.newcells == NULL) 3735 | { 3736 | wlsErrorf(NULL,_T("Cannot allocate cell structure")); 3737 | exit(1); 3738 | } 3739 | 3740 | record_malloc(1,(void*)g.newcells); 3741 | 3742 | g.newcellcount = ALLOCSIZE; 3743 | } 3744 | 3745 | g.newcellcount--; 3746 | cell = g.newcells++; 3747 | 3748 | /* 3749 | * Fill in the cell as if it was a boundary cell. 3750 | */ 3751 | cell->state = OFF; 3752 | cell->free = FALSE; 3753 | cell->frozen = FALSE; 3754 | cell->active = TRUE; 3755 | cell->unchecked = FALSE; 3756 | cell->gen = -1; 3757 | cell->row = -1; 3758 | cell->col = -1; 3759 | cell->past = cell; 3760 | cell->future = cell; 3761 | cell->cul = cell; 3762 | cell->cu = cell; 3763 | cell->cur = cell; 3764 | cell->cl = cell; 3765 | cell->cr = cell; 3766 | cell->cdl = cell; 3767 | cell->cd = cell; 3768 | cell->cdr = cell; 3769 | cell->loop = cell; 3770 | 3771 | return cell; 3772 | } 3773 | 3774 | /* 3775 | * Initialize the implication table. 3776 | */ 3777 | static void 3778 | initimplic(void) 3779 | { 3780 | int nunk, non, noff, cunk, con, coff, funk, fon, foff, naon, caon, faon, desc; 3781 | BOOL valid, cison, cisoff, fison, fisoff, nison, nisoff; 3782 | 3783 | for (desc=0; descnon) nisoff = FALSE; 3832 | if (naon