Q L H A C K E R ' S J O U R N A L =========================================== Supporting All QL Programmers =========================================== #22 October 1995 The QL Hacker's Journal (QHJ) is published by Tim Swenson as a service to the QL Community. The QHJ is freely distributable. Past issues are available on disk, via e-mail, or via the Anon-FTP server, garbo.uwasa.fi. The QHJ is always on the look out for article submissions. QL Hacker's Journal c/o Tim Swenson 5615 Botkins Rd Huber Heights, OH 45424 USA (513) 233-2178 swensontc@mail.serve.com http://www.serve.com/swensont/ EDITOR'S FORUMN The biggest news this issue is I'VE GOT A HOME PAGE. I got a wild hair a while back and decided to get myself a home page. Since I could not use a server at work to host my home page (the government sort of frowns on that), I had to find a commerical service to do it. I had full Internet access from work, so I just needed a server to host the home page. Luckily, just days after getting this wild hair I saw two advertisements for just such a service. I found one place in Philidelphia, PA that would host my home page (with a 1 Meg limit) and provide mail forwarding, all for $24 a year. My new e-mail address and home page is: swensont@mail.serve.com http://www.serve.com/swensont/ My old e-mail addresses still work, but having the mail forwarding, I can move my main e-mail account but still have a single permanent e-mail address. The home page is aimed at both friends and family, so it does not say "QL Hacker's Journal" at the entry page, but you will find the QHJ on the home page. I've also put other Sinclair related informantion on the page, such as: Sinclair Internet Resources List Master Sinclair E-Mail list Z88 Source Book For those with Web access, give it a try. Having had the Pointer Environment (PE) for over a year, I have been wondering what tools were available for doing development in the PE. I have found and bought two tools for doing PE programming; QMENU and EASYPTR. In this issue I will discuss PE programming with QMENU. Since EASYPTR is a far more complex toolkit than QMENU, it may take me a while to really get the grasp of it, but I hope to have some article on it in a future issue. Big thanks to Jerome Grimbert for writing an article on how he wrote the QL Pente program. It does a good job of documenting how a Pointer Environment program is done. Jerome is fairly quick. He wrote the Pente program in only 2 days and wrote the article in only a few days after I asked if he would write such an article. STRUCTURED SUPERBASIC VERSION 2.5 Structured SuperBasic (SSB) has been sitting on my shelf for a number of years without having any upgrades. I really only upgrade SSB when there is a new feature that I need SSB to support. Since I've had the Pointer Environment (PE) for just over a year, I've always wanted to do some programming supporting it. Only recently have I picked up two utilites that will help me do this; QMENU and EasyPointer II. My intention is to create a version of SSB that will work under the PE. But, before I could get there, I needed to upgrade SSB to give me some new features. Version 2.5 provides some of these features and is designed to have a limited life until version 3.0 comes out. There will be a version 2.6. I've finally decided to use a few ToolKit II extensions in SSB, hoping to make it better. Version 2.6 will incorporate these extensions. The biggest upgrade in version 2.5 is the support of conditional "compilation." Anyone familiar with the C Preprocessor is familiar with conditional compilation. Through the use of #DEFINE, #IFDEF, and #ENDIF statements, various parts of source code can be left out of the final code. With the setting of one variable, a program can be compiled to support MS-DOS while changing the variable can allow it to support UNIX. Note that I put quotes around the word "compilation." Since SSB is really just a filter that converts SSB source code to a SuperBasic ready form, there really is no compilation. Version 2.5 also supports imbedded print filter directives. I use a couple of "home-grown" print filters for printing out various texts. I have adopted the NROFF and WordStar conventions of using dot (.) commands for imbedding formating commands into the text. A dot command is something like this: .boldon This command tells the print filter to set the bold feature on. SSB version 2.5 allows you to imbedd these commands in your SSB code so that you can send the source code through one of these filters and allow you to produce a nice listing. This concept is similar to Web programming. Web, designed by Donald Knuth, believes that the code should be self documenting, and to this end believes that the documentation and source code should be one and the same. Dot commands must start in the first character of a line. Version 2.5 supports the / directive of line continuation. In the old days of line editors, if you went past the 80th character in your code, you put in a / to tell the compiler that the rest of the code for that line is on the next line. Since SSB is written using editors based on 80 character screens, it is handy to not have real long code lines, especially when indenting to show nesting and program structure. To use /, the / character must be the last character of the line. The next line will be read in and appended to the current line. The one caveot is that you may not have two line continuation marks in a row. This means the following: This will not work: IF var = something THEN / LET some_variable = / some_other_variable But this will work: IF var = something THEN / LET some_variable = some_other_variable IF var = something_else THEN / LET some_variable = nothing The big difference is that the continuation line does not have a continuation itself. ** Test file for showing Conditonal "Compilation" #define TEST ##define NOTEST #ifdef TEST print "This is test" print "TEST is defined" #endif #ifdef NOTEST print "This is not a test" print "NOTEST is defined" #endif #ifdef TEST print "This is a second test" print "TEST is defined" #endif print "This code should appear" print "This code should also appear This version of SSB has not been rigorously tested. I've tested the conditional "compilation" using the test file above. There might be a case in which this program fails. Since this is a limited life program (before the next upgrade), I'm not too worried about it's bugs. Consider this to be a beta version of 3.0. If you encounter any problems, bugs, or have comments on SSB or my lack of good programming, please let me know. One note of thanks to Herb Schaaf who helped me over a stumbling block in my code. He fixed the problem I could not figure out. Sometimes it can hinder you when you are too close to the code. It makes you read what is not there. ## Structured SuperBasic Filter ## Author: Timothy Swenson ## Date Created: 16 Oct 1990 ## Date Revised: 5 Sept 1995 ## Version: 2.5 ## This program takes in SSB code and outputs a file ## that is runnable in SuperBasic ## Init Variables DIM label$(30,16) DIM label(30) DIM defn$(30,16) label_var = 1 define_var = 1 num_count = 0 file_num = 5 OPEN #3,con_300x200a75x0_32 BORDER #3,2,4 PAPER #3,0 : INK #3,4 : CLS #3 PRINT #3," STRUCTURED SUPERBASIC FILTER" PRINT #3," Version 2.5" PRINT #3," by Timothy Swenson" PRINT #3 PRINT #3,"Enter input file: ( _ssb) " INPUT #3,in_file$ PRINT #3,"Enter output file: (default ";in_file$;"_bas) " INPUT #3,out_file$ PRINT #3,"Enter Starting Line Number: (default 100) " INPUT #3,line_num$ PRINT #3,"Enter Increment for Line Numbers: (default 10) " INPUT #3,line_delta$ ## Default for the output file is the input file with ## a _bas extension. IF out_file$ = "" THEN out_file$=in_file$&"_bas" in_file$ = in_file$&"_ssb" ## Default for the starting line number is 100. IF line_num$ = "" THEN line_num_d = 100 ELSE line_num_d = line_num$ END IF ## Default for line number increment is 10. IF line_delta$ = "" THEN line_delta = 10 ELSE line_delta = line_delta$ END IF line_num = line_num_d PRINT #3," PASS 1" pass_one in_file$, file_num line_num = line_num_d PRINT #3 PRINT #3," PASS 2" num_count = 0 file_num = 5 DELETE out_file$ OPEN_NEW #4,out_file$ pass_two in_file$, file_num CLOSE #4 PRINT #3 PRINT #3,"Program Done" CLOSE #3 STOP ## Pass_one is strictly designed to count line numbers ## and to find all labels. This allows goto labels to ## refer to code that the filter has not yet seen ## (as compared to a single pass filter). DEFine PROCedure pass_one (in_file$, file_num) OPEN_IN #file_num,in_file$ REPeat pass_1 num_count = num_count + 1 IF (num_count MOD 10) = 0 THEN PRINT #3,CHR$(1); IF EOF(#file_num) THEN EXIT pass_1 INPUT #file_num,in$ ## Get first non-space character temp=first_char(in$) ## Check for Blank Lines IF temp=0 THEN NEXT pass_1 ## If the ** comment line then don't count as a line IF in$(temp TO temp+1)="**" THEN NEXT pass_1 ## Ignore dot commands for print filters IF in$(temp)="." THEN NEXT pass_1 ## A \ is at the end of a line meaning to add the next line IF in$(LEN(in$))="\" THEN IF EOF(#file_num) THEN abort_out(1) INPUT #file_num,in2$ temp = first_char(in2$) in$ = in$( TO LEN(in$))&in2$(temp TO) END IF ## If the AT symbol is found, then it's a label IF in$(1)=CHR$(64) THEN label$(label_var) = in$ label(label_var) = line_num label_var = label_var + 1 ELSE ## If it's the include statement then call pass_one again IF upper$(in$(1 TO 8))="#INCLUDE" THEN pass_one in$(10 to ), file_num+1 ELSE IF upper$(in$(1 TO 7))="#DEFINE" THEN defn$(define_var) = upper$(in$( 9 TO)) define_var = define_var+1 ELSE IF upper$(in$(1 TO 6))="#IFDEF" THEN temp1 = 0 temp$ = upper$(in$(8 TO)) FOR x = 1 TO define_var IF temp$ = defn$(x) THEN temp1 = 1 NEXT X IF temp1 = 0 THEN REPEAT loop IF EOF(#file_num) THEN abort_out(1) INPUT #file_num,in2$ IF upper$(in2$(1 TO 6))="#ENDIF" THEN EXIT loop END REPeat loop END IF ELSE line_num = line_num + line_delta END IF END IF END IF END IF END REPeat pass_1 CLOSE #file_num END DEFine pass_one ## Pass_two does the main work of the filter. ## It adds the line numbers and the goto references. DEFine PROCedure pass_two (in_file$, file_num) OPEN_IN #file_num,in_file$ REPeat pass_2 num_count = num_count + 1 IF (num_count MOD 10) = 0 THEN PRINT #3,CHR$(1); IF EOF(#file_num) THEN EXIT pass_2 INPUT #file_num,in$ temp=first_char(in$) ## Ignore blank Lines IF temp=0 THEN NEXT pass_2 ## Ignore Labels IF in$(1)=CHR$(64) THEN NEXT pass_2 ## Ignore ** comments IF in$(temp TO temp+1)="**" THEN NEXT pass_2 ## Ignore dot commands IF in$(temp)="." THEN NEXT pass_2 ## Ignore #DEFINE commands since DEFINE table is already established IF upper$(in$(1 TO 7))="#DEFINE" THEN NEXT pass_2 ## A \ is at the end of a line meaning to add the next line IF in$(LEN(in$))="\" THEN IF EOF(#file_num) THEN abort_out(1) INPUT #file_num,in2$ temp = first_char(in2$) in$ = in$( TO LEN(in$))&in2$(temp TO) END IF IF in$(temp TO temp+1)="##" THEN PRINT #4,line_num;" remark ";in$(1 TO temp-1);in$(temp+2 TO ) line_num = line_num + line_delta ELSE temp = CHR$(64) INSTR in$ IF temp<>0 THEN a$ = in$(temp TO ) temp2 = 0 @label1 temp2 = temp2 + 1 IF temp2 > 30 THEN PRINT #3,"Warning - Label ";a$;" Not Found" END REPeat pass_2 END IF IF label$(temp2) <> a$ THEN GO TO @label1 PRINT #4,line_num;" ";in$(1 TO temp-1);label(temp2) line_num = line_num + line_delta ELSE IF upper$(in$(1 to 8))="#INCLUDE" THEN pass_two in$(10 to), file_num+1 ELSE IF upper$(in$(1 TO 6))="#IFDEF" THEN temp1 = 0 temp$ = upper$(in$(8 TO)) FOR x = 1 TO define_var IF temp$ = defn$((x),1 TO LEN(temp$)) THEN temp1 = x NEXT X IF temp1 = 0 THEN REPEAT loop IF EOF(#file_num) THEN abort_out(1) INPUT #file_num,in2$ IF upper$(in2$(1 TO 6))="#ENDIF" THEN EXIT loop END REPeat loop END IF ELSE IF upper$(in$(1 TO6))<>"#ENDIF" THEN PRINT #4,line_num;" ";in$ line_num = line_num + line_delta END IF END IF END IF END IF END IF END REPeat pass_2 CLOSE #file_num END DEFine pass_two ## Function first_char ## returns the location of the first non-white ## space character. DEFine FuNction first_char (a$) LOCal count count=0 @label2 count=count+1 IF count > LEN(a$) THEN RETurn 0 IF a$(count)=" " THEN GO TO @label2 RETurn count END DEFine first_char ## Function upper$ ## Takes in input string and returns the ## same string in all upper case letters. DEF FuNction upper$(up$) LOCal x, temp FOR x = 1 TO LEN(up$) temp = CODE(up$(x)) IF temp > 96 AND temp < 123 THEN up$(x)=CHR$(temp-32) NEXT x RETURN up$ END DEFine ## Procedure abort_out ## A separate procedure to neatly close all ## of the files and exit the program. ## Usually reserved for fatal errors. DEF PROCedure abort_out(err_code) ## error = 1 End of File while doing an #IFDEF IF err_code = 1 THEN PRINT #3,"FATAL ERROR - #ENDIF not found before end of file" IF file_num = 5 THEN CLOSE #5 ELSE FOR x = 5 TO file_num CLOSE #x NEXT X END IF CLOSE #4 CLOSE #5 CLOSE #3 STOP END IF END DEFine PHILOSOPHY OF STRUCTURED SUPERBASIC Philosophy is defined as a system of principles for guidance in practical affairs. The Philosophy of Structured SuperBasic (SSB) is the principles behind the creation of SSB. This philosophy guides the development of SSB. Knowing the philosophy behind the design and implementation of SSB helps to understand the what's and why's of SSB. 1. SSB Must "compile" itself. This principle comes directly from Small-C. When Small-C was written, one of it's principles was that Small-C was to be written in Small-C. When I first wrote SSB, I initially wrote the source code in SSB. I then hand translated it to SuperBasic to produce the first working program. From then on, SSB has always compiled newer versions of itself. 2. Implement What is Not Implemented. SSB was always designed to support features not built into SuperBasic, but not support those that were supported. A good example of this is the use of the #DEFINE directive. In C #DEFINE VAR is used to set conditional "compilation" when ever the #IFDEF VAR is encounted. #DEFINE can also be used to define constants ( #DEFINE YES 1 ). When I implemented #DEFINE I supported only the conditional "compilation" aspect of #DEFINE but not its support in defining constants, since the defining of constants can be done easily in SuperBasic ( LET YES = 1 ). 3. Structured SuperBasic Must Support The Lowest QL. I have never liked writting distributable software that relied on having any add-on's to the QL. This includes using ToolKit II, QLiberator, or Minerva extensions. Version 2.6 will be the first version to support TKII, but it also has the conditional "compilation" option of supporting the bare bones QL. Version 3.0 which will support the Pointer Environment will also support the Non-Pointer Environment QLs. PROGRAMMING WITH QMENU QMENU is a Pointer Environment (PE) programming tool put out by Jochem Merz. QMENU is a number of SuperBasic extensions that provide PE compatability. The extensions are fairly simple to understand and to program with. It really does not take too much time to get programming in the PE. As easy as QMENU is to use I don't know if it will become my prefered PE programming tool. My first real program to write in the PE is going to be Structured SuperBasic (SSB). I have sat down and started thinking about how I was going to convert SSB to the PE, what QMENU extensions I will use, and how I want the windows to look. I did run into one fairly significant problem: QMENU extensions are designed for input only. The QMENU extensions are designed to get input from the user, be it asking for a string, a file extension, a file name, or something else. With the call of an extension, your program waits for user input. You can't pop up a window and then continue processing. There are no extensions that allow the creation of a window for output. You can't open up a window and display the output from your program. I do like the look and feel of QMENU. The extension for getting a file name opens up a big window and allows the user to browse through the directory of a disk, select a new disk drive, etc. It's a powerful extension. The other extensions are great and suit the need they were designed for. But without the inclusion of an extension to get our output to the user, it can be kind of hard to write a number of programs with QMENU. You could just leave the screen blank while your program did it's processing or number crunching, but that leaves the user wondering if the program has locked up or is just taking a while to chug. It's better to have a display on the screen saying that the program is working ( a flashing WORKING is what I like to use). The key thing going for QMENU is it's easy of use. I am worried about the time it will take for me to get up to speed with EASYPTR and get using it (it's manual is fairly thick for a QL manual). I may use QMENU as my main PE toolkit because of it's easy of use and despite it's major fault. DAY OF THE WEEK In the April 1995 issue of "Dr. Dobb's Journal", in the "Algorithm Alley" column, Kim Larsen presents an algorithm on determining the day of the week, given a day, month, and year. The algorithm is fairly straight forward and will work from any date from September 14th, 1752 (the day we switched to the current calendar system). The article goes into detail on how the algorithm was created. If anyone is interested in the full discussion, I can photocopy the article and send it in the mail. Below is a SuperBasic version of the algorithm. I've also included another function that takes today's date and tell you what day of the week it is. If you need a numberic answer on the day of the week, you can easily change the return value to reflect the actual number generated and skip the conversion to a text name. DEFine FuNction day_of_week$(d, m, y) LOCal a IF m = 1 OR m = 2 THEN m=m+12 y = y - 1 END IF a = (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) MOD 7 If a = 0 THEN RETurn "Sunday" IF a = 1 THEN RETurn "Monday" IF a = 2 THEN RETurn "Tuesday" IF a = 3 THEN RETurn "Wednesday" IF a = 4 THEN RETurn "Thursday" IF a = 5 THEN RETurn "Friday" IF a = 6 THEN RETurn "Saturday" END DEFine DEFine FuNction today$ LOCal a$, year, month, month$, day LET a$ = DATE$ LET year = a$( 1 TO 4) LET month$ = a$(6 TO 8) LET day = a$(10 TO 11) IF month$ = "Jan" THEN month=1 IF month$ = "Feb" THEN month=2 IF month$ = "Mar" THEN month=3 IF month$ = "Apr" THEN month=4 IF month$ = "May" THEN month=5 IF month$ = "Jun" THEN month=6 IF month$ = "Jul" THEN month=7 IF month$ = "Aug" THEN month=8 IF month$ = "Sep" THEN month=9 IF month$ = "Oct" THEN month=10 IF month$ = "Nov" THEN month=11 IF month$ = "Dec" THEN month=12 LET to_day$ = day_of_week$(day,month,year) RETurn to_day$ END DEFine WRITING A PENTE PROGRAM FOR THE QL By Jerome Grimbert First, let me introduce myself. I'm 25 years old, live in France (as I'm French) and have had a QL since 1986/87. My main use of the QL is games and game programming, so I'm very glad to have C68. My prefered type of game is one of reflexion and not dexterity (may be because I'm not very quick with my finger. I always loose). Anyway, sometime ago I was able to get back issues of the QLHJ (or QHJ). Reading issue #19 I saw a call for a Pente program. I decided to make one. Let me first explain the rules of Pente: On a standard goban (chinese board of 19x19 intersections), each player takes turns placing a stone (like Go stones) of his/her color on the board. The winner is the player who gets five stones in a row, in any of the eight directions, or who has taken 5 pairs of stones from his/her opponent. Example of winning alignment: + + X + + O + + O O O O O X + + X + + O + + + + + + + + + + + + + O + + X Horizontaly + + O + X + + + O + + X + + <--- Diagonaly A pair of stones is taken (captured) when it is inside two opposite stones. Start Position + + + X O O + + X's Move + + + X O O X + End of Move 2 + + + X + + X + But making a pair this way is does not result in a capture. Start Position + + + X O + X + O's Move + + + X O O X + It's the capturing move (drop of the X stone) that takes the pair of O's. The forming of a pair of O's inside a pair of X's is allowed and does not result in a capture. Taking more than one pair with one move (stone) is possible. X + + X + + + + O + O + + + ! is a winning play for each player. + + O O + + + X will win by 5 captures. + + + ! O O X O will win by alignment. + + O O + + + + O + O + + + Additionally, the first move is always on the center of the board (this is a forced move) and the third move (first player's second move) is always at least three intersections away from the center. The reduces the advantage of the first player. + + + + + + + X cannot play it's second move on . + . . . . . + O can play it's first move anywhere. + . . . . . + + . . X . . + + . . . . . + + . . . . . + + + + + + + + So now you are ready to play Pente. The big problem when playing is choosing which way you want to win, by alignment or capture. You have to be carefull about what your opponent does, stopping him/her from doing alignment may expose you to capture. This will allow your opponent to try again an alignment, because your blocking stones have been taken, or will make you loose because you lost too many pairs. This game may seem easy, but it can be very difficult and tense. So now you know the rules. Now about programming the game. First, I designed the general screen layout. Having a QL, my screen resolution is 512x256, and I wanted to use the entire screen, I base my board size and stone size on this resolution. 256/19 is the limit of the stone size. The stones were created with an Icon Editor ( or Sprite Editor to stick to QL terms) from QTPR. The board will be white with black lines and the stones will be red and green (this is a little like the colors used in the original board version of the game). I then designed the sprites for the stone and the board. I used a sprite for the board because drawing lines is kind of slow. Board sprites are mainly intersections, except on the border (where there is only three lines connecting), on the corner (where there is only two), and at the GoBan node (the dotted intersections). Board sprites will allow for quick rewrites of the screen when captures take place, since only those intersections affected need to be redrawn instead of the whole board. The next step was the C programming. As I have existing game sources, I copy the Main file which contains the window description. This file does not change very much. In main(), I declare the Windows, Status, Loose Items, and so on, with the definition of the acting routines (which may call other bigger functions). I defined the window description, putting in the application window in which the board is drawn and the standard loose item menu (Sleep, Move, Help, Quit (ESC), and Wait). To these loose items I add Computer Play For Me, which has the computer play your next move for you, and How Many Players, which the user uses to select one or two players. I don't have Computer vs Computer now, but sometime in the future. What is also standard is the Info Item with the name of the application. I also add other Info Items showing the number of taken pairs by each side and an About selection. I liked the About from QD and FIFI, so I added it in. The Info Item for the Count of pairs taken is just two strings of text that will be redrawn when the count changes. Now I have a nice looking empty board with mainly all of the Loose Items working. Resize, Move, Quit, Number of Players, Wake are almost always the same, so I just copy them. In fact, as I started with the Windows Demo of C68, I have a module to Pop-up a menu choice either vertically, horizontaly or even in a 2x3 frame with up to six different choices. It allows a standard prompt and the possible choice; i.e, the Quit prompt is "OK to Quit" and the choices are "Yes", "No", or "Sleep" with a default of "No." All is taken care of by the routine, so the caller only needs to see a very sympathetic line of C: choice = menu_choice_h("Ok to Quit",3,2,"Yes","No","Sleep"); The function returns -1 to choice if the ESC key was pressed, else it returns the C index in the list of choices. Next I need to write the 'About', a new thing for me, which will be a pop-up window with some text. I derived it from the menu_choice() routine, removing all of the paramaters (it's hard coded) and adding the text and sprite item. I kept the last line as a Loose Menu Item so a mouse user could close the window without having to hit the ESC key (which should be the instinctive way to close this window). Now I had to put the stones on the board. The first player is Red, so at the start of Pente, the application cursor (in the application window) is the red stone sprite. The hit routine will then get the cursor coordinates, transform them into Board coordinates, and call a specific routine to 1) check for the validity of the move, 2) to see if pairs have been taken, 3) put the stone on the screen, 4) and either make the computer play (if there is only one player, or change the color of the pointer to the green stone sprite. If the move is not legal (especially for the first and third stones) then a pop-up window is shown, just saying that the move was illegal. ESC or DO in this window will close it. Since you Hit to put your stone, the mouse user will have to change his/her finger to close it. It forces them to read it and to think. This window is made from the menu_choice/About code. We now have the input/ouput of part of the program. The next step is to provide the brain. For Pente, I bought the brain from Mr. FrankEinstein, a.k.a. an old french revue "Jeux & Strategie" (Game & Strategy) who once printed a Basic Pente program. I took this program, removed the input/output sections, and concentrated on the internals. The player side is easy: - Verify validity of move - Put the stone - Check for victory by alignment with the new stone - If the new stone has captured any pairs, update counters and board, and check for capture victory. - Swap the player to go The computer side is not so easy. The program was written to have the computer always play the second player and that is not what I wanted. For each move the computer looks for any known alignment or partial figures, giving points for each free intersection. The higher the points the better the move. The whole intelligence of the program is the weighting between figures and the intersection with the higher sores in the move played. To be accurate, there is also and end game behavior. When the computer has already captured 4 pairs, it will give more points to a move that will allow it to take the last pair, hence making a sensitive choice of one move to take the pairs rather than many to win by alignment. The base of the evaluation is that each time a stone is put on the board the intersections near it gain points. Once this behavior is translated in C, then comes the hairy things: testing. I did recognize that I had forgotten to add multiple captures. Once the testing was done, I just had to finish with the details (the Help Loose Item is now easy now that there is a brain). And then you have a wonderful Pente Program. It only took me two days (Yes!) to write the program, but I had a lot of things already written; menu, skeleton of the program, resize routine, etc. The sprite Editer was a must. I would have wasted a lot of time computing hex numbers to get my sprites. My logo ( a rose ) is a big sprite that came from a previous program, as did the Help and Player sprites used as Loose Items. It appeared that the most difficult thing was the Hit routine interface, as it evolved with time and the version of C68 (it seems to be stable, but I don't use it a lot, so I'm less familiar with it than with the Loose Item or even the Menu Item action routines). It's by smithing that you become a smith. For once, I have not done a Mode 8 design. Maybe I shall, but the color is just fine for the moment and I don't think I will double the size of the text items and recreate the sprites in Mode 8, even if this is very simple. Should Masterpice come soon (the new graphics card), I may well use the 512x512 in Mode 8 as well as the 1024x512 in Mode 4. For those of you that are interested you may get the Pente Executable from me by email (just write to jgrimbert@wtk.suresnes.marben.fr). It's absolutely free. You may also ask for the sources, but I reserve the right to not give them out. Just send email to the same address. Please expect a little delay before the answer, as the first request will make me go home, get the files, go back to work, and send them. Also, due to personal/professional travel, requests before 1 December 1995 may be delayed. Please Note: Tim Swenson has the first public version of the program, but there is an annoying flickering of the counter when the board is redrawn. [ I'm happy just to have a Pente program. I can live with the flicker - ED] Ask me for the lastest version.