Browse Source

Plan 9 from Bell Labs 2007-10-03

David du Colombier 16 years ago
parent
commit
6798947892

+ 16 - 14
dist/replica/_plan9.db

@@ -191,7 +191,7 @@
 386/bin/cpp - 775 sys sys 1188446882 149057
 386/bin/cpu - 775 sys sys 1188446885 139216
 386/bin/crop - 775 sys sys 1168402298 116374
-386/bin/cwfs - 775 sys sys 1190401259 365500
+386/bin/cwfs - 775 sys sys 1191295136 365724
 386/bin/date - 775 sys sys 1178568265 41845
 386/bin/db - 775 sys sys 1188499570 349188
 386/bin/dc - 775 sys sys 1168402299 99260
@@ -7393,7 +7393,7 @@ sys/man/1/filter - 664 sys sys 1159419912 6143
 sys/man/1/fmt - 664 sys sys 1070032221 1557
 sys/man/1/fortune - 664 sys sys 944959673 449
 sys/man/1/freq - 664 sys sys 1136378272 735
-sys/man/1/games - 664 sys sys 1137080360 5373
+sys/man/1/games - 664 sys sys 1191356354 5444
 sys/man/1/grap - 664 sys sys 944959675 6417
 sys/man/1/graph - 664 sys sys 1148227125 3061
 sys/man/1/grep - 664 sys sys 1139690020 2257
@@ -7598,7 +7598,7 @@ sys/man/2/open - 664 sys sys 1015091524 3404
 sys/man/2/perror - 664 sys sys 944959696 1881
 sys/man/2/pipe - 664 sys sys 950892864 1587
 sys/man/2/plumb - 664 sys sys 1015091524 4816
-sys/man/2/pool - 664 sys sys 1162102403 8574
+sys/man/2/pool - 664 sys sys 1191360187 8577
 sys/man/2/postnote - 664 sys sys 944959695 790
 sys/man/2/prime - 664 sys sys 1165622556 1961
 sys/man/2/print - 664 sys sys 1115941566 8852
@@ -7700,7 +7700,7 @@ sys/man/4/INDEX.html - 664 sys sys 1183240481 5147
 sys/man/4/acme - 664 sys sys 1181238729 10435
 sys/man/4/archfs - 664 sys sys 960000712 533
 sys/man/4/cdfs - 664 sys sys 1026846913 3638
-sys/man/4/cfs - 664 sys sys 1190855816 2132
+sys/man/4/cfs - 664 sys sys 1191355900 2136
 sys/man/4/consolefs - 664 sys sys 1144424854 4245
 sys/man/4/cwfs - 664 sys sys 1189029640 6255
 sys/man/4/dossrv - 664 sys sys 1168307403 4334
@@ -7801,7 +7801,7 @@ sys/man/7/scat - 664 sys sys 970069855 8904
 sys/man/8 - 20000000775 sys sys 1162240005 0
 sys/man/8/0intro - 664 sys sys 944959679 247
 sys/man/8/6in4 - 664 sys sys 1186182195 2330
-sys/man/8/9load - 664 sys sys 1094676489 9170
+sys/man/8/9load - 664 sys sys 1191355906 9162
 sys/man/8/9pcon - 664 sys sys 1145881850 2234
 sys/man/8/INDEX - 664 sys sys 1183260468 2773
 sys/man/8/INDEX.html - 664 sys sys 1183240488 8619
@@ -7813,7 +7813,7 @@ sys/man/8/boot - 664 sys sys 1188515985 8331
 sys/man/8/booting - 664 sys sys 1015024984 4136
 sys/man/8/cpurc - 664 sys sys 1187227584 1522
 sys/man/8/cron - 664 sys sys 1063858596 1867
-sys/man/8/dhcpd - 664 sys sys 1172959497 5572
+sys/man/8/dhcpd - 664 sys sys 1191306105 5575
 sys/man/8/disksim - 664 sys sys 1144150487 1476
 sys/man/8/drawterm - 664 sys sys 1181675938 1921
 sys/man/8/fossilcons - 664 sys sys 1187134504 18542
@@ -8111,7 +8111,7 @@ sys/src/9/pc/i8259.c - 664 sys sys 1131290399 4586
 sys/src/9/pc/init9.c - 664 sys sys 1040002518 94
 sys/src/9/pc/initcode.s - 664 sys sys 1015014519 282
 sys/src/9/pc/io.h - 664 sys sys 1165555523 9406
-sys/src/9/pc/kbd.c - 664 sys sys 1132574883 12365
+sys/src/9/pc/kbd.c - 664 sys sys 1191355133 12339
 sys/src/9/pc/l.s - 664 sys sys 1190268443 29347
 sys/src/9/pc/main.c - 664 sys sys 1168306227 15337
 sys/src/9/pc/mem.h - 664 sys sys 1137622431 5203
@@ -12555,7 +12555,7 @@ sys/src/cmd/ip/snoopy/icmp6.c - 664 sys sys 1181425765 7771
 sys/src/cmd/ip/snoopy/il.c - 664 sys sys 1139667365 2260
 sys/src/cmd/ip/snoopy/ip.c - 664 sys sys 1181425781 4292
 sys/src/cmd/ip/snoopy/ip6.c - 664 sys sys 1181425803 5329
-sys/src/cmd/ip/snoopy/main.c - 664 sys sys 1146318297 15720
+sys/src/cmd/ip/snoopy/main.c - 664 sys sys 1191350326 16160
 sys/src/cmd/ip/snoopy/mkfile - 664 sys sys 1181425645 1015
 sys/src/cmd/ip/snoopy/ninep.c - 664 sys sys 1138463390 555
 sys/src/cmd/ip/snoopy/ospf.c - 664 sys sys 1138463391 7369
@@ -14505,11 +14505,12 @@ sys/src/games/catclock.c - 664 sys sys 1108184106 10354
 sys/src/games/eyes.p - 664 sys sys 1108184106 1128
 sys/src/games/juggle.c - 664 sys sys 1118153689 4471
 sys/src/games/mahjongg - 20000000775 sys sys 1123098896 0
-sys/src/games/mahjongg/graphics.c - 664 sys sys 1137080359 7054
-sys/src/games/mahjongg/level.c - 664 sys sys 1137080360 2560
-sys/src/games/mahjongg/mahjongg.c - 664 sys sys 1137080360 3631
-sys/src/games/mahjongg/mahjongg.h - 664 sys sys 1137080360 1687
-sys/src/games/mahjongg/mkfile - 664 sys sys 1095792293 230
+sys/src/games/mahjongg/graphics.c - 664 sys sys 1191355576 5749
+sys/src/games/mahjongg/level.c - 664 sys sys 1191355577 2490
+sys/src/games/mahjongg/logic.c - 664 sys sys 1191354594 1720
+sys/src/games/mahjongg/mahjongg.c - 664 sys sys 1191355577 3869
+sys/src/games/mahjongg/mahjongg.h - 664 sys sys 1191355577 2092
+sys/src/games/mahjongg/mkfile - 664 sys sys 1191354593 241
 sys/src/games/memo.c - 664 sys sys 1130853201 6471
 sys/src/games/mkfile - 664 sys sys 1140203009 595
 sys/src/games/music - 20000000775 sys sys 1103793915 0
@@ -15733,4 +15734,5 @@ usr/glenda/lib/profile - 664 glenda glenda 1105128663 890
 usr/glenda/readme.acme - 664 glenda glenda 1019860628 4753
 usr/glenda/readme.rio - 664 glenda glenda 1019860628 6370
 usr/glenda/tmp - 20000000775 glenda glenda 1018802620 0
-386/bin/cwfs - 775 sys sys 1191295136 365724
+386/bin/games/mahjongg - 775 sys sys 1191381911 165242
+386/bin/snoopy - 775 sys sys 1191381914 194808

+ 15 - 14
dist/replica/plan9.db

@@ -258,7 +258,7 @@
 386/bin/games/juggle - 775 sys sys 1179372089 125804
 386/bin/games/jukebox - 775 sys sys 1105589128 264821
 386/bin/games/jukefs - 775 sys sys 1105589129 165320
-386/bin/games/mahjongg - 775 sys sys 1179372089 165271
+386/bin/games/mahjongg - 775 sys sys 1191381911 165242
 386/bin/games/memo - 775 sys sys 1179372089 156656
 386/bin/games/playlistfs - 775 sys sys 1105589129 148484
 386/bin/games/sokoban - 775 sys sys 1179372090 165514
@@ -409,7 +409,7 @@
 386/bin/snap - 775 sys sys 1188530194 316182
 386/bin/snapfs - 775 sys sys 1188530202 390401
 386/bin/sniffer - 775 sys sys 1038443185 99028
-386/bin/snoopy - 775 sys sys 1187639427 190560
+386/bin/snoopy - 775 sys sys 1191381914 194808
 386/bin/sort - 775 sys sys 1178568303 81057
 386/bin/spin - 775 sys sys 1186543935 769291
 386/bin/split - 775 sys sys 1181507270 75811
@@ -7393,7 +7393,7 @@ sys/man/1/filter - 664 sys sys 1159419912 6143
 sys/man/1/fmt - 664 sys sys 1070032221 1557
 sys/man/1/fortune - 664 sys sys 944959673 449
 sys/man/1/freq - 664 sys sys 1136378272 735
-sys/man/1/games - 664 sys sys 1137080360 5373
+sys/man/1/games - 664 sys sys 1191356354 5444
 sys/man/1/grap - 664 sys sys 944959675 6417
 sys/man/1/graph - 664 sys sys 1148227125 3061
 sys/man/1/grep - 664 sys sys 1139690020 2257
@@ -7598,7 +7598,7 @@ sys/man/2/open - 664 sys sys 1015091524 3404
 sys/man/2/perror - 664 sys sys 944959696 1881
 sys/man/2/pipe - 664 sys sys 950892864 1587
 sys/man/2/plumb - 664 sys sys 1015091524 4816
-sys/man/2/pool - 664 sys sys 1162102403 8574
+sys/man/2/pool - 664 sys sys 1191360187 8577
 sys/man/2/postnote - 664 sys sys 944959695 790
 sys/man/2/prime - 664 sys sys 1165622556 1961
 sys/man/2/print - 664 sys sys 1115941566 8852
@@ -7700,7 +7700,7 @@ sys/man/4/INDEX.html - 664 sys sys 1183240481 5147
 sys/man/4/acme - 664 sys sys 1181238729 10435
 sys/man/4/archfs - 664 sys sys 960000712 533
 sys/man/4/cdfs - 664 sys sys 1026846913 3638
-sys/man/4/cfs - 664 sys sys 1190855816 2132
+sys/man/4/cfs - 664 sys sys 1191355900 2136
 sys/man/4/consolefs - 664 sys sys 1144424854 4245
 sys/man/4/cwfs - 664 sys sys 1189029640 6255
 sys/man/4/dossrv - 664 sys sys 1168307403 4334
@@ -7801,7 +7801,7 @@ sys/man/7/scat - 664 sys sys 970069855 8904
 sys/man/8 - 20000000775 sys sys 1162240005 0
 sys/man/8/0intro - 664 sys sys 944959679 247
 sys/man/8/6in4 - 664 sys sys 1186182195 2330
-sys/man/8/9load - 664 sys sys 1094676489 9170
+sys/man/8/9load - 664 sys sys 1191355906 9162
 sys/man/8/9pcon - 664 sys sys 1145881850 2234
 sys/man/8/INDEX - 664 sys sys 1183260468 2773
 sys/man/8/INDEX.html - 664 sys sys 1183240488 8619
@@ -7813,7 +7813,7 @@ sys/man/8/boot - 664 sys sys 1188515985 8331
 sys/man/8/booting - 664 sys sys 1015024984 4136
 sys/man/8/cpurc - 664 sys sys 1187227584 1522
 sys/man/8/cron - 664 sys sys 1063858596 1867
-sys/man/8/dhcpd - 664 sys sys 1172959497 5572
+sys/man/8/dhcpd - 664 sys sys 1191306105 5575
 sys/man/8/disksim - 664 sys sys 1144150487 1476
 sys/man/8/drawterm - 664 sys sys 1181675938 1921
 sys/man/8/fossilcons - 664 sys sys 1187134504 18542
@@ -8111,7 +8111,7 @@ sys/src/9/pc/i8259.c - 664 sys sys 1131290399 4586
 sys/src/9/pc/init9.c - 664 sys sys 1040002518 94
 sys/src/9/pc/initcode.s - 664 sys sys 1015014519 282
 sys/src/9/pc/io.h - 664 sys sys 1165555523 9406
-sys/src/9/pc/kbd.c - 664 sys sys 1132574883 12365
+sys/src/9/pc/kbd.c - 664 sys sys 1191355133 12339
 sys/src/9/pc/l.s - 664 sys sys 1190268443 29347
 sys/src/9/pc/main.c - 664 sys sys 1168306227 15337
 sys/src/9/pc/mem.h - 664 sys sys 1137622431 5203
@@ -12555,7 +12555,7 @@ sys/src/cmd/ip/snoopy/icmp6.c - 664 sys sys 1181425765 7771
 sys/src/cmd/ip/snoopy/il.c - 664 sys sys 1139667365 2260
 sys/src/cmd/ip/snoopy/ip.c - 664 sys sys 1181425781 4292
 sys/src/cmd/ip/snoopy/ip6.c - 664 sys sys 1181425803 5329
-sys/src/cmd/ip/snoopy/main.c - 664 sys sys 1146318297 15720
+sys/src/cmd/ip/snoopy/main.c - 664 sys sys 1191350326 16160
 sys/src/cmd/ip/snoopy/mkfile - 664 sys sys 1181425645 1015
 sys/src/cmd/ip/snoopy/ninep.c - 664 sys sys 1138463390 555
 sys/src/cmd/ip/snoopy/ospf.c - 664 sys sys 1138463391 7369
@@ -14505,11 +14505,12 @@ sys/src/games/catclock.c - 664 sys sys 1108184106 10354
 sys/src/games/eyes.p - 664 sys sys 1108184106 1128
 sys/src/games/juggle.c - 664 sys sys 1118153689 4471
 sys/src/games/mahjongg - 20000000775 sys sys 1123098896 0
-sys/src/games/mahjongg/graphics.c - 664 sys sys 1137080359 7054
-sys/src/games/mahjongg/level.c - 664 sys sys 1137080360 2560
-sys/src/games/mahjongg/mahjongg.c - 664 sys sys 1137080360 3631
-sys/src/games/mahjongg/mahjongg.h - 664 sys sys 1137080360 1687
-sys/src/games/mahjongg/mkfile - 664 sys sys 1095792293 230
+sys/src/games/mahjongg/graphics.c - 664 sys sys 1191355576 5749
+sys/src/games/mahjongg/level.c - 664 sys sys 1191355577 2490
+sys/src/games/mahjongg/logic.c - 664 sys sys 1191354594 1720
+sys/src/games/mahjongg/mahjongg.c - 664 sys sys 1191355577 3869
+sys/src/games/mahjongg/mahjongg.h - 664 sys sys 1191355577 2092
+sys/src/games/mahjongg/mkfile - 664 sys sys 1191354593 241
 sys/src/games/memo.c - 664 sys sys 1130853201 6471
 sys/src/games/mkfile - 664 sys sys 1140203009 595
 sys/src/games/music - 20000000775 sys sys 1103793915 0

+ 15 - 0
dist/replica/plan9.log

@@ -52978,3 +52978,18 @@
 1191274203 26 c 386/lib/libhttpd.a - 664 sys sys 1191273745 100822
 1191279604 0 c sys/src/9/port/devfs.c - 664 sys sys 1191279047 14902
 1191295804 0 c 386/bin/cwfs - 775 sys sys 1191295136 365724
+1191306604 0 c sys/man/8/dhcpd - 664 sys sys 1191306105 5575
+1191351604 0 c sys/src/cmd/ip/snoopy/main.c - 664 sys sys 1191350326 16160
+1191355203 0 c sys/src/9/pc/kbd.c - 664 sys sys 1191355133 12339
+1191355203 1 a sys/src/games/mahjongg/logic.c - 664 sys sys 1191354594 1720
+1191355203 2 c sys/src/games/mahjongg/mkfile - 664 sys sys 1191354593 241
+1191357005 0 c sys/man/1/games - 664 sys sys 1191356354 5444
+1191357005 1 c sys/man/4/cfs - 664 sys sys 1191355900 2136
+1191357005 2 c sys/man/8/9load - 664 sys sys 1191355906 9162
+1191357005 3 c sys/src/games/mahjongg/graphics.c - 664 sys sys 1191355576 5749
+1191357005 4 c sys/src/games/mahjongg/level.c - 664 sys sys 1191355577 2490
+1191357005 5 c sys/src/games/mahjongg/mahjongg.c - 664 sys sys 1191355577 3869
+1191357005 6 c sys/src/games/mahjongg/mahjongg.h - 664 sys sys 1191355577 2092
+1191360604 0 c sys/man/2/pool - 664 sys sys 1191360187 8577
+1191382203 0 c 386/bin/games/mahjongg - 775 sys sys 1191381911 165242
+1191382203 1 c 386/bin/snoopy - 775 sys sys 1191381914 194808

+ 6 - 0
sys/man/1/games

@@ -123,6 +123,12 @@ quit,
 .LR H
 gives a hint, either trying to match the currently selected tile, or if no tile is
 selected finding out the first available tile.
+.LR U
+and
+.LR Bksp
+undo the last move,
+.LR C
+tries to solve the level.
 .TP
 .B sokoban
 Guide Glenda through a room full of walls, pebbles and holes to put

+ 1 - 0
sys/man/2/pool

@@ -15,6 +15,7 @@ void*	poolalloc(Pool* pool, ulong size)
 .B
 void*	poolallocalignspan(Pool *pool, ulong size, 
 .br
+.B
                 ulong align, long offset, ulong span)
 .PP
 .B

+ 2 - 2
sys/man/4/cfs

@@ -75,7 +75,7 @@ Exclusive with
 .BR -a .
 .TP
 .B k
-Keep cache contents even if they might have come from a different server.
+keep cache contents even if they might have come from a different server.
 .I Cfs
 will obey
 .B -r
@@ -84,7 +84,7 @@ even if
 is given.
 .TP
 .B n
-Mount remote file server without authentication;
+mount the remote file server without authentication;
 often useful with
 .BR -F .
 .TP

+ 5 - 6
sys/man/8/9load

@@ -119,7 +119,7 @@ loads the
 .I bootfile
 at the entry address specified by the header,
 usually
-.BR 0x80100020 .
+.BR 0xF0100020 .
 After loading, control is passed to the entry location.
 .PP
 Finally,
@@ -255,7 +255,7 @@ or
 .IR plan9.ini (8))
 on a partition named
 .B dos
-or 
+or
 .BR 9fat .
 If one is found, searching stops and the file is read into memory
 at physical address 0x1200
@@ -354,14 +354,14 @@ for the
 .IR bootfile .
 This is used by
 .IR sd (3)
-to initialize partitions so that 
+to initialize partitions so that
 .IR kfs (4)
 file systems can be mounted as the root file system.
 A more extensive partitioning is typically done by
 .I fdisk
 and
 .I prep
-as part of 
+as part of
 .I termrc
 or
 .I cpurc
@@ -380,7 +380,7 @@ When loaded from a PBS (rather than from
 .I 9load
 must be contiguously allocated on
 the disk.
-See 
+See
 .IR dossrv (4)
 for information on ensuring this.
 .PP
@@ -419,7 +419,6 @@ the contents are obtained and used as a
 .IR plan9.ini (8),
 .IR prep (8)
 .SH BUGS
-.PP
 Much of the work done by
 .B 9load
 is duplicated by the loaded kernel.

+ 1 - 1
sys/man/8/dhcpd

@@ -16,7 +16,7 @@ dhcpd, dhcpleases, rarpd, tftpd \- Internet booting
 [
 .I address
 .I n
-]*
+] ...
 .PP
 .B ip/dhcpleases
 .PP

+ 0 - 1
sys/src/9/pc/kbd.c

@@ -466,7 +466,6 @@ i8042intr(Ureg*, void*)
 			return;
 		case Altgr:
 			kbscan.altgr = 1;
-			kbscan.collecting = 0;
 			return;
 		case Kmouse|1:
 		case Kmouse|2:

+ 26 - 3
sys/src/cmd/ip/snoopy/main.c

@@ -1,9 +1,13 @@
+/*
+ * snoopy - network sniffer
+ */
 #include <u.h>
 #include <libc.h>
 #include <ip.h>
 #include <bio.h>
 #include <fcall.h>
 #include <libsec.h>
+#include <ndb.h>
 #include "dat.h"
 #include "protos.h"
 #include "y.tab.h"
@@ -735,6 +739,7 @@ void
 compile_cmp(char *proto, Filter *f, Field *fld)
 {
 	uchar x[IPaddrlen];
+	char *v;
 
 	if(f->op != '=')
 		sysfatal("internal error: compile_cmp %s: not a cmp", proto);
@@ -748,13 +753,31 @@ compile_cmp(char *proto, Filter *f, Field *fld)
 				f->ulv = atoi(f->r->s);
 				break;
 			case Fether:
-				parseether(f->a, f->r->s);
+				v = csgetvalue(nil, "sys", (char*)f->r->s,
+					"ether", 0);
+				if(v){
+					parseether(f->a, v);
+					free(v);
+				} else
+					parseether(f->a, f->r->s);
 				break;
 			case Fv4ip:
-				f->ulv = parseip(x, f->r->s);
+				v = csgetvalue(nil, "sys", (char*)f->r->s,
+					"ip", 0);
+				if(v){
+					f->ulv = parseip(x, v);
+					free(v);
+				}else
+					f->ulv = parseip(x, f->r->s);
 				break;
 			case Fv6ip:
-				parseip(f->a, f->r->s);
+				v = csgetvalue(nil, "sys", (char*)f->r->s,
+					"ipv6", 0);
+				if(v){
+					parseip(f->a, v);
+					free(v);
+				}else
+					parseip(f->a, f->r->s);
 				break;
 			case Fba:
 				parseba(f->a, f->r->s);

+ 221 - 263
sys/src/games/mahjongg/graphics.c

@@ -5,186 +5,176 @@
 
 #include "mahjongg.h"
 
-int
-freeup(int d, Point p)
-{
-	/* are we blocked from above? */
-	if(d == Depth -1 || (level.board[d+1][p.x][p.y].which == 0 &&
-			level.board[d+1][p.x+1][p.y].which == 0 &&
-			level.board[d+1][p.x][p.y+1].which == 0 &&
-			level.board[d+1][p.x+1][p.y+1].which == 0))
-		return 1;
-	
-	return 0;
-}
 
-int
-freeleft(int d, Point p)
+/*
+ * mark tiles that partially obscure the given tile.
+ * relies on Depth*Dxy <= Tilex/2
+ */
+void
+markabove(int d, int x, int y)
 {
-
-	/* blocked from the left? */
-	if(p.x == 0 || (level.board[d][p.x-1][p.y].which == 0 &&
-		level.board[d][p.x-1][p.y+1].which == 0)) 
-		return 1;
-
-	return 0;
-}
-
-int
-freeright(int d, Point p)
-{		
-	if(p.x == Lx-2 || (level.board[d][p.x+2][p.y].which == 0 &&
-		level.board[d][p.x+2][p.y+1].which == 0))
-		return 1;
-
-	return 0;
+	int dx, dy;
+
+	for(d++; d < Depth; d++)
+		for(dx = -1; dx <= 2; dx++)
+			for(dy = -1; dy <= 2; dy++)
+				if(x+dx < Lx && x+dx >= 0 &&
+				    y+dy < Ly && y+dy >= 0)
+					level.board[d][x+dx][y+dy].redraw = 1;
 }
 
-int
-isfree(int d, Point p)
+void
+markbelow(int d, int x, int y)
 {
-	return (freeleft(d, p) || freeright(d, p)) && freeup(d, p);
+	int dx, dy;
+
+	for(d--; d >= 0; d--)
+		for(dx = -2; dx <= 1; dx++)
+			for(dy = -2; dy <= 1; dy++)
+				if(x+dx < Lx && x+dx >= 0 &&
+				    y+dy < Ly && y+dy >= 0)
+					level.board[d][x+dx][y+dy].redraw = 1;
 }
 
-void
-clearbrick(int d, Point p)
+Rectangle
+tilerect(Click c)
 {
-	level.board[d][p.x][p.y].which = 0;
-	level.board[d][p.x+1][p.y].which = 0;
-	level.board[d][p.x][p.y+1].which = 0;
-	level.board[d][p.x+1][p.y+1].which = 0;
+	Point p;
+	Rectangle r;
+
+	p = Pt(c.x*(Facex/2)-(c.d*TileDxy), c.y*(Facey/2)-(c.d*TileDxy));
+	r = Rpt(p, addpt(p, Pt(Facex, Facey)));
+	return rectaddpt(r, Pt(Depth*TileDxy, Depth*TileDxy));
 }
 
 void
-resize(Point p)
+clearbrick(Click c)
 {
-	/* resize to the size of the current level */
+	Rectangle r;
 
-	int fd;
+	level.hist[--level.remaining] = c;
 
-	fd = open("/dev/wctl", OWRITE);
-	if(fd >= 0){
-		fprint(fd, "resize -dx %d -dy %d", p.x, p.y);
-		close(fd);
-	}
+	level.board[c.d][c.x][c.y].which = None;
+	level.board[c.d][c.x+1][c.y].which = None;
+	level.board[c.d][c.x][c.y+1].which = None;
+	level.board[c.d][c.x+1][c.y+1].which = None;
+
+	r = tilerect(c);
+	draw(img, r, background, nil, r.min);
 
+	markabove(c.d, c.x, c.y);
+	markbelow(c.d, c.x, c.y);
 }
 
 void
-drawbrick(int d, int x, int y)
+drawbrick(Click c)
 {
-	Point p;
 	Rectangle r;
 
-	p = Pt(x*(Facex/2)-(d*TileDxy), y*(Facey/2)-(d*TileDxy));
-	r = Rpt(p, addpt(p, Pt(Facex, Facey)));
-	r = rectaddpt(r, Pt(Depth*TileDxy, Depth*TileDxy));
-	draw(img, r, tileset, nil, level.board[d][x][y].start);
+	r = tilerect(c);
+	draw(img, r, tileset, nil, level.board[c.d][c.x][c.y].start);
 
-	if(level.board[d][x][y].clicked)
+	if(level.board[c.d][c.x][c.y].clicked)
 		draw(img, r, selected, nil, ZP);
 
-	if(level.l.d == d && eqpt(level.l.p, Pt(x, y)))
-		border(img, r, 2, litbrdr, level.board[d][x][y].start);
+	if(eqcl(level.l, c))
+		border(img, r, 2, litbrdr, level.board[c.d][c.x][c.y].start);
 
 	/* looks better without borders, uncomment to check it out with'em */
 //	r = Rpt(r.min, addpt(r.min, Pt(Tilex, Tiley)));
 //	draw(img, r, brdr, nil, ZP);
 }
 
-
 void
-drawlevel(void)
+redrawlevel(int all)
 {
+	Brick *b;
 	int d, x, y;
 
-	draw(img, img->r, background, nil, ZP);
-
 	for(d = 0; d < Depth; d++)
 		for(y = 0; y < Ly; y++)
-			for(x = 0; x < Lx; x++)
-				if(level.board[d][x][y].which == 1) 
-					drawbrick(d, x, y);
+			for(x = 0; x < Lx; x++) {
+				b = &level.board[d][x][y];
+				if(b->which == TL && (all || b->redraw)) {
+					drawbrick(Cl(d,x,y));
+					markabove(d,x,y);
+				}
+				b->redraw = 0;
+			}
 
 	draw(screen, screen->r, img, nil, ZP);
 	flushimage(display, 1);
 }
 
-Brick *
-bmatch(int d, Point p)
+void
+updatelevel(void)
 {
-	int x, y;
-	int ld = d;
-
-	do {
-		for(y = 0; y < Ly; y++)
-			for(x = 0; x < Lx; x++)
-				if(level.board[ld][x][y].which == 1 && isfree(ld, Pt(x, y)) && !eqpt(Pt(x, y), p) && 
-					level.board[d][p.x][p.y].type == level.board[ld][x][y].type)
-			
-					return &level.board[ld][x][y];
-
-	} while(--ld >= 0);
+	redrawlevel(0);
+}
 
-	return nil;
+void
+drawlevel(void)
+{
+	draw(img, img->r, background, nil, ZP);
+	redrawlevel(1);
 }
 
-int
-canmove(void)
+void
+resize(Point p)
 {
-	int d, x, y;
+	int fd;
 
-	for(d = Depth - 1; d >= 0; d--) 
-		for(y = 0; y < Ly; y++) 
-			for(x = 0; x < Lx; x++) 
-				if(level.board[d][x][y].which == 1 && isfree(d, Pt(x, y)))
-					if(bmatch(d, Pt(x, y)) != nil)
-						return 1;
-	return 0;
+	fd = open("/dev/wctl", OWRITE);
+	if(fd >= 0){
+		fprint(fd, "resize -dx %d -dy %d", p.x, p.y);
+		close(fd);
+	}
 }
 
 void
 hint(void)
 {
-	Brick *b = nil;
 	int d = 0, x = 0, y = 0;
+	Brick *b = nil;
 
 	if(level.c.d != -1) {
-		if((b = bmatch(level.c.d, level.c.p)) != nil) {
+		if((b = bmatch(level.c)) != nil) {
 			d = level.c.d;
-			x = level.c.p.x;
-			y = level.c.p.y;
+			x = level.c.x;
+			y = level.c.y;
 		}
-	} else {
-		for(d = Depth - 1; d >= 0; d--) 
-			for(y = 0; y < Ly; y++) 
-				for(x = 0; x < Lx; x++) 
-					if(level.board[d][x][y].which == 1 && isfree(d, Pt(x, y)))
-						if((b = bmatch(d, Pt(x, y))) != nil)
-							goto Matched;
-	}
-
+	} else 
+		for(d = Depth - 1; d >= 0; d--)
+			for(y = 0; y < Ly; y++)
+				for(x = 0; x < Lx; x++)
+					if(level.board[d][x][y].which == TL &&
+					    isfree(Cl(d,x,y)) &&
+					    (b = bmatch(Cl(d,x,y))) != nil)
+						goto Matched;
 Matched:
-	if(b != nil) {
-		level.board[d][x][y].clicked = 1;
-		b->clicked = 1;
-		drawlevel();
-		sleep(500);
-		if(level.c.d == -1)
-			level.board[d][x][y].clicked = 0;
-		b->clicked = 0;
-		drawlevel();
-		sleep(500);
-		level.board[d][x][y].clicked = 1;
-		b->clicked = 1;
-		drawlevel();
-		sleep(500);
-		if(level.c.d == -1)
-			level.board[d][x][y].clicked = 0;
-		b->clicked = 0;
-		drawlevel();
-	}
-
+	if (b == nil)
+		return;
+	level.board[d][x][y].clicked = 1;
+	b->clicked = 1;
+	b->redraw = 1;
+	updatelevel();
+	sleep(500);
+	if(level.c.d == -1)
+		level.board[d][x][y].clicked = 0;
+	b->clicked = 0;
+	b->redraw = 1;
+	updatelevel();
+	sleep(500);
+	level.board[d][x][y].clicked = 1;
+	b->clicked = 1;
+	b->redraw = 1;
+	updatelevel();
+	sleep(500);
+	if(level.c.d == -1)
+		level.board[d][x][y].clicked = 0;
+	b->clicked = 0;
+	b->redraw = 1;
+	updatelevel();
 }
 
 void
@@ -196,182 +186,150 @@ done(void)
 	flushimage(display, 1);
 }
 
-
-void
-clicked(Point coord)
+Click
+findclick(Point coord)
 {
-	Point p;
-	int d;
-
-	/* ugly on purpose */
+	Click c;
 
-	for(d = Depth - 1; d >= 0; d--) {
-		p = Pt((coord.x + TileDxy*d)/(Facex/2), (coord.y + TileDxy*d)/(Facey/2));
-		switch(level.board[d][p.x][p.y].which) {
-		case 0:
+	for(c.d = Depth - 1; c.d >= 0; c.d--) {
+		c.x = (coord.x + TileDxy*c.d)/(Facex/2);
+		c.y = (coord.y + TileDxy*c.d)/(Facey/2);
+		switch(level.board[c.d][c.x][c.y].which) {
+		case None:
 			break;
-		case 1:
-			goto Found;
-		case 2:
-			p = Pt(p.x-1, p.y);
-			goto Found;
-		case 3:
-			p = Pt(p.x-1, p.y-1);
-			goto Found;
-		case 4:
-			p = Pt(p.x, p.y-1);
-			goto Found;
+		case TL:
+			return c;
+		case TR:
+			c.x = c.x - 1;
+			return c;
+		case BR:
+			c.x = c.x - 1;
+			c.y = c.y - 1;
+			return c;
+		case BL:
+			c.y = c.y - 1;
+			return c;
 		}
 	}
+	return NC;
+}
 
-	return;
-
-Found:
-	if(freeup(d, p) && (freeleft(d, p) || freeright(d, p))) {
-		if(level.c.d == -1) {
-			level.c.d = d;
-			level.c.p = p;
-			level.board[d][p.x][p.y].clicked = 1;
-		} else if(!eqpt(p, level.c.p) && 
-			(level.board[d][p.x][p.y].type == level.board[level.c.d][level.c.p.x][level.c.p.y].type)) {
-
-			clearbrick(d, p);
-			clearbrick(level.c.d, level.c.p);
+void
+clicked(Point coord)
+{
+	Click c;
+	Brick *b, *bc;
 
-			level.c.d = -1;
-			level.c.p = Pt(0, 0);
+	c = findclick(coord);
+	if (c.d == -1)
+		return;
 
-			level.remaining -= 2;
+	b = &level.board[c.d][c.x][c.y];
+	if(isfree(c)) {
+		if(level.c.d == -1) {
+			level.c = c;
+			b->clicked = 1;
+			b->redraw = 1;
+		} else if(eqcl(c, level.c)) {
+			level.c = NC;
+			b->clicked = 0;
+			b->redraw = 1;
 		} else {
-			level.board[d][p.x][p.y].clicked = 0;
-			level.board[level.c.d][level.c.p.x][level.c.p.y].clicked = 0;
-			level.c.d = -1;
-			level.c.p = Pt(0, 0);
-		} 
-		drawlevel();
+			bc = &level.board[level.c.d][level.c.x][level.c.y];
+			if(b->type == bc->type) {
+				clearbrick(c);
+				bc->clicked = 0;
+				clearbrick(level.c);
+				level.c = NC;
+			} else {
+				bc->clicked = 0;
+				bc->redraw = 1;
+				b->clicked = 1;
+				b->redraw = 1;
+				level.c = c;
+			}
+		}
+		updatelevel();
 		if(!canmove())
 			done();
 	}
 }
 
 void
-light(Point coord)
+undo(void)
 {
-	Point p;
-	int d;
+	int i, j, d, x, y;
 
-	/* ugly on purpose */
-
-	for(d = Depth - 1; d >= 0; d--) {
-		p = Pt((coord.x + TileDxy*d)/(Facex/2), (coord.y + TileDxy*d)/(Facey/2));
-		switch(level.board[d][p.x][p.y].which) {
-		case 0:
-			break;
-		case 1:
-			goto Found;
-		case 2:
-			p = Pt(p.x-1, p.y);
-			goto Found;
-		case 3:
-			p = Pt(p.x-1, p.y-1);
-			goto Found;
-		case 4:
-			p = Pt(p.x, p.y-1);
-			goto Found;
-		}
-	}
-
-	return;
-
-Found:
-	if(level.l.d == d && eqpt(level.l.p, p))
+	if(level.remaining >= Tiles)
 		return;
 
-	if(freeup(d, p) && (freeleft(d, p) || freeright(d, p))) {
-		Point tmpp;
-		int tmpd;
-
-		tmpd = level.l.d;
-		tmpp = level.l.p;
-
-		level.l.d = d;
-		level.l.p = p;
-		drawbrick(d, p.x, p.y);
-
-		/* clean up the previously lit brick */
-		if(tmpd != -1 && level.board[tmpd][tmpp.x][tmpp.y].which == 1) 
-			drawbrick(tmpd, tmpp.x, tmpp.y);
-			
-		draw(screen, screen->r, img, nil, ZP);
-		flushimage(display, 1);
-	} else if(level.l.d != -1) {
-		d = level.l.d;
-		p = level.l.p;
-		level.l.d = -1;
-		level.l.p = Pt(0, 0);
-
-		if(level.board[d][p.x][p.y].which == 1) {
-			drawbrick(d, p.x, p.y);
-			draw(screen, screen->r, img, nil, ZP);
-			flushimage(display, 1);
-		}
+	for(i=1; i<=2; i++) {
+		j = level.remaining++;
+		d = level.hist[j].d;
+		x = level.hist[j].x;
+		y = level.hist[j].y;
+		level.board[d][x][y].which = TL;
+		level.board[d][x+1][y].which = TR;
+		level.board[d][x+1][y+1].which = BR;
+		level.board[d][x][y+1].which = BL;
+		level.board[d][x][y].redraw = 1;
 	}
+	updatelevel();
 }
 
-/* below only for testing */
-
-Point
-pmatch(int d, Point p)
+void
+deselect(void)
 {
-	int x, y;
-	int ld = d;
-
-	do {
-		for(y = 0; y < Ly; y++)
-			for(x = 0; x < Lx; x++)
-				if(level.board[ld][x][y].which == 1 && isfree(ld, Pt(x, y)) && !eqpt(Pt(x, y), p) && 
-					level.board[d][p.x][p.y].type == level.board[ld][x][y].type)
-			
-					return Pt(x, y);
-
-	} while(--ld >= 0);
+	Brick *b;
 
-	return Pt(-1, -1);
+	if(level.c.d == -1)
+		return;
+	b = &level.board[level.c.d][level.c.x][level.c.y];
+	level.c = NC;
+	b->clicked = 0;
+	b->redraw = 1;
+	updatelevel();
 }
 
-int
-dmatch(int d, Point p)
+void
+light(Point coord)
 {
-	int x, y;
-	int ld = d;
+	Click c = findclick(coord);
+	if (c.d == -1)
+		return;
 
-	do {
-		for(y = 0; y < Ly; y++)
-			for(x = 0; x < Lx; x++)
-				if(level.board[ld][x][y].which == 1 && isfree(ld, Pt(x, y)) && !eqpt(Pt(x, y), p) && 
-					level.board[d][p.x][p.y].type == level.board[ld][x][y].type)
-			
-					return ld;
+	if(eqcl(level.l, c))
+		return;
 
-	} while(--ld >= 0);
+	if (level.l.d != -1) {
+		level.board[level.l.d][level.l.x][level.l.y].redraw = 1;
+		level.l = NC;
+	}
+
+	if(isfree(c)) {
+		level.l = c;
+		level.board[c.d][c.x][c.y].redraw = 1;
+	}
 
-	return -1;
+	updatelevel();
 }
 
 void
 clearlevel(void)
 {
-	int d, x, y;
-
-	for(d = Depth - 1; d >= 0; d--) 
-		for(y = 0; y < Ly; y++) 
-			for(x = 0; x < Lx; x++) 
-				if(level.board[d][x][y].which == 1 && isfree(d, Pt(x, y)))
-					if(bmatch(d, Pt(x, y)) != nil) {
-						clearbrick(dmatch(d, Pt(x, y)), pmatch(d, Pt(x, y)));
-						clearbrick(d, Pt(x, y));
-						level.remaining -= 2;
-						drawlevel();
+	Click c, cm;
+
+	for(c.d = Depth - 1; c.d >= 0; c.d--)
+		for(c.y = 0; c.y < Ly; c.y++)
+			for(c.x = 0; c.x < Lx; c.x++)
+				if(level.board[c.d][c.x][c.y].which == TL &&
+				    isfree(c)) {
+					cm = cmatch(c, c.d);
+					if(cm.d != -1) {
+						clearbrick(cm);
+						clearbrick(c);
+						updatelevel();
 						clearlevel();
 					}
+				}
 }

+ 17 - 20
sys/src/games/mahjongg/level.c

@@ -16,9 +16,9 @@ consumeline(Biobuf *b)
 int
 parse(char *layout)
 {
-	Biobuf *b;
 	int x = 0, y = 0, depth = 0;
 	char c;
+	Biobuf *b;
 
 	b = Bopen(layout, OREAD);
 	if(b == nil) {
@@ -62,7 +62,7 @@ indextype(int type)
 {
 	int t;
 
-	if(type < 108) 	
+	if(type < 108)
 		t = (type/36)*Facey * 9 + ((type%36)/4)*Facex;
 	else if(type < 112)
 		t = Seasons;
@@ -81,28 +81,26 @@ Point
 indexpt(int type)
 {
 	Point p;
-	
-	/* the first 108 bricks are 4 of each, 36 per line:
+
+	/*
+	 * the first 108 bricks are 4 of each, 36 per line:
 	 * 	x = (index%36)/4
 	 *	y = (index)/36
 	 * then multiply by the size of a single tile.
 	 * the next 4 are the seasons, so x = index%4...
-	 *
 	 * and so on...
 	 */
-		
-	if(type < 108) {
+
+	if(type < 108)
 		p = Pt(((type%36)/4)*Facex, (type/36)*Facey);
-	} else if(type < 112) {
+	else if(type < 112)
 		p = Pt((type%4)*Facex, 3*Facey);
-	} else if(type < 128) {
+	else if(type < 128)
 		p = Pt((((type+12)%36)/4)*Facex, 3*Facey);
-	} else if(type < 132) {
+	else if(type < 132)
 		p = Pt(((type+4)%4)*Facex, 4*Facey);
-	} else {
+	else
 		p = Pt((((type+28)%36)/4)*Facex, 4*Facey);
-	}
-
 	return p;
 }
 
@@ -110,9 +108,9 @@ indexpt(int type)
 void
 generate(uint seed)
 {
-	Point p;
 	int x, y, d, n;
 	int order[144];
+	Point p;
 
 	srand(seed);
 
@@ -125,7 +123,7 @@ generate(uint seed)
 		order[x] = order[y];
 		order[y] = n;
 	}
-	
+
 	n = 0;
 	for(d = 0; d < Depth; d++)
 		for(y = 0; y < Ly; y++)
@@ -141,12 +139,11 @@ generate(uint seed)
 				}
 
 	if(n != orig.remaining)
-		fprint(2, "level improperly generated: %d elements, should have %d\n", n, orig.remaining);
+		fprint(2, "level improperly generated: %d elements, "
+			"should have %d\n", n, orig.remaining);
 
-	orig.c.d = -1;
-	orig.c.p = Pt(0, 0);
-	orig.l.d = -1;
-	orig.l.p = Pt(0, 0);
+	orig.c = NC;
+	orig.l = NC;
 	orig.done = 0;
 	level = orig;
 }

+ 99 - 0
sys/src/games/mahjongg/logic.c

@@ -0,0 +1,99 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+
+#include "mahjongg.h"
+
+Click NC = { -1, 0, 0, };
+
+Click
+Cl(int d, int x, int y)
+{
+	return (Click){d, x, y};
+}
+
+int
+eqcl(Click c1, Click c2)
+{
+	return c1.d == c2.d && c1.x == c2.x && c1.y == c2.y;
+}
+
+int
+freeup(Click c)
+{
+	if(c.d == Depth -1 || (level.board[c.d+1][c.x][c.y].which == None &&
+	    level.board[c.d+1][c.x+1][c.y].which == None &&
+	    level.board[c.d+1][c.x][c.y+1].which == None &&
+	    level.board[c.d+1][c.x+1][c.y+1].which == None))
+		return 1;
+	return 0;
+}
+
+int
+freeleft(Click c)
+{
+	if(c.x == 0 || (level.board[c.d][c.x-1][c.y].which == None &&
+	    level.board[c.d][c.x-1][c.y+1].which == None))
+		return 1;
+	return 0;
+}
+
+int
+freeright(Click c)
+{
+	if(c.x == Lx-2 || (level.board[c.d][c.x+2][c.y].which == None &&
+	    level.board[c.d][c.x+2][c.y+1].which == None))
+		return 1;
+	return 0;
+}
+
+int
+isfree(Click c)
+{
+	return (freeleft(c) || freeright(c)) && freeup(c);
+}
+
+Click
+cmatch(Click c, int dtop)
+{
+	Click lc;
+
+	lc.d = dtop;
+	do {
+		for(lc.y = 0; lc.y < Ly; lc.y++)
+			for(lc.x = 0; lc.x < Lx; lc.x++)
+				if(level.board[lc.d][lc.x][lc.y].which == TL &&
+				    isfree(lc) && !eqcl(c, lc) &&
+				    level.board[c.d][c.x][c.y].type ==
+				    level.board[lc.d][lc.x][lc.y].type)
+					return lc;
+	} while(--lc.d >= 0);
+	return NC;
+}
+
+Brick *
+bmatch(Click c)
+{
+	Click lc;
+
+	lc = cmatch(c, Depth);
+	if(lc.d == -1)
+		return nil;
+	else
+		return &level.board[lc.d][lc.x][lc.y];
+}
+
+int
+canmove(void)
+{
+	Click c;
+
+	for(c.d = Depth - 1; c.d >= 0; c.d--)
+		for(c.y = 0; c.y < Ly; c.y++)
+			for(c.x = 0; c.x < Lx; c.x++)
+				if(level.board[c.d][c.x][c.y].which == TL &&
+				    isfree(c) && bmatch(c) != nil)
+					return 1;
+	return 0;
+}

+ 63 - 42
sys/src/games/mahjongg/mahjongg.c

@@ -2,23 +2,25 @@
 #include <libc.h>
 #include <draw.h>
 #include <event.h>
-
+#include <stdio.h>
 #include "mahjongg.h"
 
-char *Border = "/sys/games/lib/mahjongg/images/border.bit";
-char *Mask = "/sys/games/lib/mahjongg/images/mask.bit";
-char *Gameover = "/sys/games/lib/mahjongg/images/gameover.bit";
+#define MJDIR "/sys/games/lib/mahjongg/"
 
-char *deftileset = "/sys/games/lib/mahjongg/tilesets/default.tileset";
-char *defbackgr = "/sys/games/lib/mahjongg/backgrounds/default.bit";
-char *deflayout = "/sys/games/lib/mahjongg/layouts/default.layout";
-ulong defchan;
+char *Border	= MJDIR "images/border.bit";
+char *Mask	= MJDIR "images/mask.bit";
+char *Gameover	= MJDIR "images/gameover.bit";
 
-int trace;
+char *deftileset= MJDIR "tilesets/default.tileset";
+char *defbackgr = MJDIR "backgrounds/default.bit";
+char *deflayout = MJDIR "layouts/default.layout";
 
+ulong defchan;
+int trace;
 
-char *buttons[] = 
+char *buttons[] =
 {
+	"deselect",
 	"new",
 	"restart",
 	"resize",
@@ -26,15 +28,15 @@ char *buttons[] =
 	0
 };
 
-Menu menu = 
+Menu menu =
 {
 	buttons
 };
 
 void
-usage(char *progname)
+usage(void)
 {
-	fprint(2, "usage: %s [-b background] [-l layout] [-t tileset] [-c] [-f]\n", progname);
+	fprint(2, "usage: %s [-cf] [-b bg] [-l layout] [-t tileset]\n", argv0);
 	exits("usage");
 }
 
@@ -46,7 +48,6 @@ eallocimage(Rectangle r, int repl, uint chan, uint color)
 	tmp = allocimage(display, r, chan, repl, color);
 	if(tmp == nil)
 		sysfatal("cannot allocate buffer image: %r");
-
 	return tmp;
 }
 
@@ -65,19 +66,20 @@ eloadfile(char *path)
 	if(img == nil)
 		sysfatal("cannot load image: %r");
 	close(fd);
-	
+
 	return img;
 }
 
-
 void
 allocimages(void)
 {
 	Rectangle one = Rect(0, 0, 1, 1);
-	
+
 	selected = eallocimage(one, 1, RGBA32, setalpha(DPalebluegreen, 0x5f));
 	litbrdr = eallocimage(one, 1, RGBA32, DGreen);
-	img = eallocimage(Rect(0, 0, Sizex, Sizey), 0, defchan ? defchan : screen->chan, DBlack);
+	img = eallocimage(Rect(0, 0, Sizex, Sizey), 0,
+		defchan? defchan: screen->chan, DBlack);
+	textcol = eallocimage(one, 1, RGBA32, DWhite);
 
 	background = eloadfile(defbackgr);
 	replclipr(background, 1, img->r);
@@ -93,40 +95,40 @@ eresized(int new)
 {
 	if(new && getwindow(display, Refnone) < 0)
 		sysfatal("can't reattach to window");
-	
 	drawlevel();
 }
 
-void 
+void
 main(int argc, char **argv)
 {
+	int clickety = 0;
 	Mouse m;
 	Event e;
-	int clickety = 0;
+	Point origin = Pt(Bord, Bord);
 
 	ARGBEGIN{
-	case 'h':
-		usage(argv0);
+	case 'b':
+		defbackgr = EARGF(usage());
+		break;
+	case 'c':
+		defchan = RGBA32;
+		break;
 	case 'f':
 		trace = 1;
 		break;
-	case 'b':
-		defbackgr = EARGF(usage(argv0));
-		break;
 	case 'l':
-		deflayout = EARGF(usage(argv0));
+		deflayout = EARGF(usage());
 		break;
 	case 't':
-		deftileset = EARGF(usage(argv0));
-		break;
-	case 'c':
-		defchan = RGBA32;
+		deftileset = EARGF(usage());
 		break;
+	default:
+		usage();
 	}ARGEND
 
-	if(argc > 0) 
-		usage(argv0);
-		
+	if(argc > 0)
+		usage();
+
 	if(! parse(deflayout)) {
 		fprint(2, "usage: %s [levelfile]\n", argv[0]);
 		exits("usage");
@@ -137,6 +139,8 @@ main(int argc, char **argv)
 	einit(Emouse|Ekeyboard);
 
 	allocimages();
+
+	/* resize to the size of the current level */
 	resize(img->r.max);
 
 	generate(time(0));
@@ -152,12 +156,14 @@ main(int argc, char **argv)
 					break;
 				if(!clickety && level.remaining > 0) {
 					clickety = 1;
-					clicked(subpt(m.xy, addpt(screen->r.min, Pt(30, 30))));
+					clicked(subpt(m.xy, addpt(screen->r.min,
+						origin)));
 				}
 			} else {
 				clickety = 0;
 				if(trace)
-					light(subpt(m.xy, addpt(screen->r.min, Pt(30, 30))));
+					light(subpt(m.xy, addpt(screen->r.min,
+						origin)));
 			}
 			if(m.buttons&2) {
 				/* nothing here for the moment */
@@ -165,17 +171,20 @@ main(int argc, char **argv)
 			if(m.buttons&4)
 				switch(emenuhit(3, &m, &menu)) {
 				case 0:
+					deselect();
+					break;
+				case 1:
 					generate(time(0));
 					drawlevel();
 					break;
-				case 1:
+				case 2:
 					level = orig;
 					drawlevel();
 					break;
-				case 2:
+				case 3:
 					resize(img->r.max);
 					break;
-				case 3:
+				case 4:
 					exits(nil);
 				}
 			break;
@@ -194,18 +203,30 @@ main(int argc, char **argv)
 			case 'N':
 				/* new */
 				generate(time(0));
+				drawlevel();
 				break;
 			case 'r':
 			case 'R':
 				level = orig;
+				drawlevel();
 				break;
 			case 'c':
 			case 'C':
-				clearlevel();
+				if(!level.done) {
+					clearlevel();
+					done();
+				}
+				break;
+			case 8:
+			case 'u':
+			case 'U':
+				if(level.done) {
+					level.done = 0;
+					drawlevel();
+				}
+				undo();
 				break;
 			}
-			if(! level.done)
-				drawlevel();
 			break;
 		}
 	}

+ 60 - 37
sys/src/games/mahjongg/mahjongg.h

@@ -1,6 +1,5 @@
 enum {
-
-	/* 
+	/*
 	 * D[1-4], the seasons, appear only once
 	 * F[1-4], the flowers, appear only once
 	 * everything else appears 4 times
@@ -8,12 +7,12 @@ enum {
 	 */
 	A1 = 0, A2, A3, A4, A5, A6, A7, A8, A9,
 	B1, B2, B3, B4, B5, B6, B7, B8, B9,
-	C1, C2, C3, C4, C5, C6, C7, C8, C9,	
-	D1, D2, D3, D4, E1, E2, E3, E4, 
+	C1, C2, C3, C4, C5, C6, C7, C8, C9,
+	D1, D2, D3, D4, E1, E2, E3, E4,
 	F1, F2, F3, F4, G1, G2, G3,
 	Seasons,
 	Flowers,
-}; 
+};
 
 enum {
 	/* level-specific enums */
@@ -22,12 +21,13 @@ enum {
 	TileDxy = 6,	/* tile displacement when on a higher level */
 	Lx = 32,
 	Ly = 16,
+	Bord = Depth*TileDxy,
 };
 enum {
 	/* the size of a complete tile */
 	Tilex = 60,
 	Tiley = 74,
-	
+
 	/* only the face part */
 	Facex = 54,
 	Facey = 68,
@@ -35,57 +35,80 @@ enum {
 	/* and the entire window, giving room for 5*6 = 30 pixels
 	 * that are needed for the higher tiles
 	 */
-	Sizex = 16*Facex + 2*Depth*TileDxy,
-	Sizey = 8*Facey + 2*Depth*TileDxy,
+	Sizex = Lx*Facex/2 + 2*Bord,
+	Sizey = Ly*Facey/2 + 2*Bord,
 };
 
+/* which part of a tile */
+typedef enum {
+	None,
+	TL,			/* main brick */
+	TR,
+	BR,
+	BL,
+} Which;
 
 typedef struct {
-	Point start;	/* where do we draw here */
-	int clicked;
-	int which;		/* 0 ↔ 4 */
-	int type;
+	Point	start;		/* where is this brick in the tileset */
+	int	clicked;
+	Which	which;
+	int	type;
+	int	redraw;
 } Brick;
 
 typedef struct {
-	int d;
-	Point p;
+	int	d;
+	int	x;
+	int	y;
 } Click;
 
 typedef struct {
-	Brick 	board[Depth][Lx][Ly];
-	Click		c; 		/* player has a brick selected */
-	Click		l; 		/* mouse-over-brick indicator */
-	int			done;
-	int 		remaining;
+	Brick 	board[Depth][Lx][Ly];	/* grid of quarter tiles */
+	Click	c; 		/* player has a brick selected */
+	Click	l; 		/* mouse-over-brick indicator */
+	int	done;
+	Click	hist[Tiles];
+	int 	remaining;
 } Level;
 
-Level level;	/* the level played */
-Level orig;		/* same, sans modifications */
+Level level;			/* the level played */
+Level orig;			/* same, sans modifications */
 
-Image *img;		/* buffer */
+Image *img;			/* buffer */
 
-Image *tileset;
+Image *background;
 Image *brdr;
+Image *gameover;
+Image *litbrdr;
 Image *mask;
-Image *background;
 Image *selected;
-Image *litbrdr;
-Image *gameover;
+Image *textcol;
+Image *tileset;
+
+/* logic.c */
+Click	Cl(int d, int x, int y);
+Click	NC;
+Brick	*bmatch(Click c);
+int	canmove(void);
+Click	cmatch(Click c, int dtop);
+int	eqcl(Click c1, Click c2);
+int	isfree(Click c);
 
 /* graphics.c */
-void drawlevel(void);
-void resize(Point);
-void clicked(Point);
-void light(Point);
-void hint(void);
-void done(void);
-void clearlevel(void);
+void	clearlevel(void);
+void	clicked(Point);
+void	deselect(void);
+void	done(void);
+void	drawlevel(void);
+void	hint(void);
+void	light(Point);
+void	resize(Point);
+void	undo(void);
 
 /* mahjongg.c */
-char *genlevels(int);
-Image *eallocimage(Rectangle, int, uint, uint);
+Image	*eallocimage(Rectangle, int, uint, uint);
+char	*genlevels(int);
 
 /* level.c */
-int parse(char *);
-void generate(uint seed);
+void	generate(uint seed);
+int	parse(char *);

+ 1 - 0
sys/src/games/mahjongg/mkfile

@@ -6,6 +6,7 @@ OFILES=\
 	mahjongg.$O\
 	graphics.$O\
 	level.$O\
+	logic.$O\
 
 
 HFILES=mahjongg.h\