summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/plugins/othelo.c348
-rw-r--r--apps/plugins/othelo.txt95
2 files changed, 200 insertions, 243 deletions
diff --git a/apps/plugins/othelo.c b/apps/plugins/othelo.c
index 8298b1d0c7..0ac7828367 100644
--- a/apps/plugins/othelo.c
+++ b/apps/plugins/othelo.c
@@ -1,183 +1,47 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2003 Blue Chip
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19/* 1/*
20 Designed, Written, AI Bots, the lot ...BlueChip =8ĒD# 2 Designed, Written, AI Bots, the lot ...BlueChip =8ĒD#
21 3
22 Thanks espcially to 4 Thanks espcially to
23 Hardeep, DevZer0, LinusN 5 DevZer0, LinusN, Zagor
24 for their help with understanding Rockbox & the SDK 6 for their help with understanding Rockbox & the SDK
25*/
26
27#include "plugin.h"
28#ifdef HAVE_LCD_BITMAP
29
30/* the following #define had to be taken from Button.c
31 'cos it is not defined in the header!! */
32/* how long until repeat kicks in */
33#define REPEAT_START 6
34
35/* player types */
36#define HUMAN false
37#define AIBOT true
38
39/* for domove() */
40#define CHECK false
41#define MAKE true
42 7
43/* screen coords - top left x&y */ 8 Please note that the code formatting is not that which was
44 /* game over */ 9 produced originally, but has been updated by whoever
45#define go_tlx 71 10 ported it to the plugin system.
46#define go_tly 17 11 I am sure it was done with good reason, so I have not
47 /* WiNS */ 12 redone it!
48#define win_tlx 63 13*/
49#define win_tly 1
50 /* DRaW */
51#define draw_tlx 59
52#define draw_tly 1
53 /* scores */
54#define sc_tlx 65
55#define sc_tly 39
56 /* logo */
57#define logo_tlx 65
58#define logo_tly 2
59
60/* board sqaures -
61 * there are a number of routines that expect these values asis
62 * do not try to play with these, you will likely kill the program
63 */
64#define PLAYERX 0
65#define PLAYERO 1
66#define POSS 2
67#define CHOICE 3
68#define EMPTY 4
69#define BORDER 5
70
71/* Who gets first turn */
72#define FIRST PLAYERX
73#define DF_PLX HUMAN
74#define DF_AIX NONE
75#define DF_PLO AIBOT
76#define DF_AIO WEAK
77
78/* Oponent skill level / help level
79 * -------- ---------------------------------------------------
80 * NONE no ai / no help
81 * WEAK random valid move / show all possible
82 * AVERAGE most pieces (random) / all + most pieces
83 * SMART most pieces (weighted/random) / all + weighted
84 * EXPERT
85 * GURU
86 */
87#define NONE 0
88#define WEAK 1
89#define AVERAGE 2
90#define SMART 3
91#define EXPERT 4
92#define GURU 5
93#define BEST 3 /* the best ai alogrithm currently available */
94
95/* these are for code clarity, do not change them! */
96#define LEFT 0x08
97#define RIGHT 0x04
98#define UP 0x02
99#define DOWN 0x01
100 14
101/* This represents the maximum number of possible moves 15/*
102 * I have no idea what the real maximum is, buts tests 16 * Version Date Who Comment
103 * suggest about 10 17 * -------- -------- ---- ------------------------------------------------
18 * 1.3 20030729 BC Fixed display bug introduced by port to plugin
19 * Updated documentation
20 * 1.2 2003 Ported to new plugin system
21 * 1.1 20030625 BC Flash board when invalid move to used aquare
22 * Fixed "pause computer" for real harware!
23 * Added USB_CONNECTED support
24 * Ensure correct fonts on the way in and out
25 * 1.0 20030622 BC Release
26 *
27 *
28 * Todo:
29 * # More AI :)
30 * # Reintroduce suspend feature under plugin system
104 */ 31 */
105#define MAXPOSS 20
106
107struct move
108{
109 int x;
110 int y;
111 int taken;
112 int rank;
113 bool player;
114};
115
116
117/*===================================================================
118 * Procedure prototypes
119 *==================================================================*/
120static void changeplayer(bool pl);
121
122static int othstrlen(char* s);
123static void othprint(unsigned char x, unsigned char y, char ch, bool upd);
124static void othprints(unsigned char x, unsigned char y, char* s, bool upd);
125
126static void initscreen(void);
127static void show_board(void);
128static void flashboard(void);
129static void show_grid(void);
130static void show_score(bool turn);
131static void show_players(void);
132static void show_f3(bool playing);
133static void hilite(struct move* move, bool on);
134static void show_endgame(unsigned char scx, unsigned char sco);
135
136static void initboard(void);
137static int getmove(struct move* move, struct move* plist,
138 unsigned char* pcnt, bool turn);
139static int checkmove(unsigned char x, unsigned char y, bool pl,
140 unsigned char dir, bool type);
141static void domove(struct move* move, bool type);
142
143static bool calcposs(struct move* plist, unsigned char* pcnt, bool turn);
144static int getplist(struct move* plist, unsigned char pl);
145static unsigned char reduceplist(struct move* plist, unsigned char pcnt,
146 unsigned char ai_help);
147static void smartranking(struct move* plist, unsigned char pcnt);
148static int plist_bytaken(const void* m1, const void* m2);
149static int plist_byrank(const void* m1, const void* m2);
150static void clearposs(void);
151 32
152 33
153/*=================================================================== 34#ifdef HAVE_LCD_BITMAP
154 * *static* local global variables
155 *==================================================================*/
156 35
36/* Plugin header */
37#include "plugin.h"
157static struct plugin_api* rb; 38static struct plugin_api* rb;
158 39
159/* score */ 40/***************************************************************************/
160static struct 41/***************************************************************************/
161 { 42/* OTHFONT.H */
162 int x; 43/***************************************************************************/
163 int o; 44/***************************************************************************/
164 } score;
165
166/* 8x8 with borders */
167static unsigned char board[10][10];
168
169/* player=HUMAN|AIBOT */
170static bool player[2] = {DF_PLX, DF_PLO};
171
172/* AI = WEAK|AVERAGE|SMART|EXPERT|GURU
173 Help=NONE|WEAK|AVERAGE|SMART|EXPERT|GURU */
174static unsigned char ai_help[2] = {DF_AIX, DF_AIO};
175
176/* is a game under way */
177static bool playing = false;
178
179/* who's turn is it? */
180static bool turn = FIRST;
181 45
182/* Don't reorder this array - you have been warned! */ 46/* Don't reorder this array - you have been warned! */
183enum othfontc { 47enum othfontc {
@@ -443,6 +307,13 @@ static unsigned char othfont[of_eos][6] = {
443 307
444}; 308};
445 309
310
311/***************************************************************************/
312/***************************************************************************/
313/* OTHLOGO.H */
314/***************************************************************************/
315/***************************************************************************/
316
446/* 317/*
447 318
448######### # # ### ## ## ### 319######### # # ### ## ## ###
@@ -575,12 +446,135 @@ static void showlogo(int x, int y, bool on)
575} 446}
576 447
577 448
449/***************************************************************************/
450/***************************************************************************/
451/* OTHELO.H */
452/***************************************************************************/
453/***************************************************************************/
454
455
456/* the following #define had to be taken from Button.c
457 'cos it is not defined in the header!! */
458/* how long until repeat kicks in */
459#define REPEAT_START 6
460
461/* player types */
462#define HUMAN false
463#define AIBOT true
464
465/* for domove() */
466#define CHECK false
467#define MAKE true
468
469/* screen coords - top left x&y */
470 /* game over */
471#define go_tlx 71
472#define go_tly 17
473 /* WiNS */
474#define win_tlx 63
475#define win_tly 1
476 /* DRaW */
477#define draw_tlx 59
478#define draw_tly 1
479 /* scores */
480#define sc_tlx 65
481#define sc_tly 39
482 /* logo */
483#define logo_tlx 65
484#define logo_tly 2
485
486/* board sqaures -
487 * there are a number of routines that expect these values asis
488 * do not try to play with these, you will likely kill the program
489 */
490#define PLAYERX 0
491#define PLAYERO 1
492#define POSS 2
493#define CHOICE 3
494#define EMPTY 4
495#define BORDER 5
496
497/* Who gets first turn */
498#define FIRST PLAYERX
499#define DF_PLX HUMAN
500#define DF_AIX NONE
501#define DF_PLO AIBOT
502#define DF_AIO WEAK
503
504/* Oponent skill level / help level
505 * -------- ---------------------------------------------------
506 * NONE no ai / no help
507 * WEAK random valid move / show all possible
508 * AVERAGE most pieces (random) / all + most pieces
509 * SMART most pieces (weighted/random) / all + weighted
510 * EXPERT
511 * GURU
512 */
513#define NONE 0
514#define WEAK 1
515#define AVERAGE 2
516#define SMART 3
517#define EXPERT 4
518#define GURU 5
519#define BEST 3 /* the best ai alogrithm currently available */
520
521/* these are for code clarity, do not change them! */
522#define LEFT 0x08
523#define RIGHT 0x04
524#define UP 0x02
525#define DOWN 0x01
526
527/* This represents the maximum number of possible moves
528 * I have no idea what the real maximum is, buts tests
529 * suggest about 10
530 */
531#define MAXPOSS 20
532
533struct move
534{
535 int x;
536 int y;
537 int taken;
538 int rank;
539 bool player;
540};
541
542
543/*===================================================================
544 * local global variables
545 * THIS IS THE DATA THAT NEEDS TO BE SAVED TO ALLOW THE GAME
546 * TO CONTINUE ...THE CONTINUE FEATURE DOES NOT WORK UNDER THE
547 * NEW PLUGIN SYSTEM!
548 *==================================================================*/
549
550/* score */
551static struct
552 {
553 int x;
554 int o;
555 } score;
556
557/* 8x8 with borders */
558static unsigned char board[10][10];
559
560/* player=HUMAN|AIBOT */
561static bool player[2] = {DF_PLX, DF_PLO};
562
563/* AI = WEAK|AVERAGE|SMART|EXPERT|GURU
564 Help=NONE|WEAK|AVERAGE|SMART|EXPERT|GURU */
565static unsigned char ai_help[2] = {DF_AIX, DF_AIO};
566
567/* is a game under way */
568static bool playing = false;
569
570/* who's turn is it? */
571static bool turn = FIRST;
572
578/******************************************************************** 573/********************************************************************
579 * strlen ofr use with othello print system 574 * strlen ofr use with othello print system
580 ********************************************************************/ 575 ********************************************************************/
581static int othstrlen(char* s) 576static int othstrlen(char* s)
582{ 577{
583
584 int i; 578 int i;
585 579
586 for(i=0; s[i]!=of_eos; i++); 580 for(i=0; s[i]!=of_eos; i++);
@@ -730,10 +724,14 @@ static void show_players(void)
730 724
731 if (player[PLAYERX]==AIBOT) 725 if (player[PLAYERX]==AIBOT)
732 scs[2] = of_c; 726 scs[2] = of_c;
727 else
728 scs[2] = of_h;
733 scs[4] = ai_help[PLAYERX] +of_0; 729 scs[4] = ai_help[PLAYERX] +of_0;
734 730
735 if (player[PLAYERO]==AIBOT) 731 if (player[PLAYERO]==AIBOT)
736 scs[8] = of_c; 732 scs[8] = of_c;
733 else
734 scs[8] = of_h;
737 scs[10] = ai_help[PLAYERO] +of_0; 735 scs[10] = ai_help[PLAYERO] +of_0;
738 736
739 othprints( 2,58, &scs[0], true); 737 othprints( 2,58, &scs[0], true);
@@ -1151,11 +1149,11 @@ static unsigned char reduceplist(struct move* plist, unsigned char pcnt, unsigne
1151 /* ------------------------------------------------- */ 1149 /* ------------------------------------------------- */
1152 case GURU: 1150 case GURU:
1153 break; 1151 break;
1154 /* ------------------------------------------------- */ 1152 /* ------------------------------------------------- */
1155 case EXPERT: 1153 case EXPERT:
1156 break; 1154 break;
1157 /* ------------------------------------------------- */ 1155 /* ------------------------------------------------- */
1158 /* this player will favour certain known moves */ 1156 /* this player will favour certain known moves */
1159 case SMART: 1157 case SMART:
1160 if (pcnt>1) 1158 if (pcnt>1)
1161 { 1159 {
@@ -1167,8 +1165,8 @@ static unsigned char reduceplist(struct move* plist, unsigned char pcnt, unsigne
1167 pcnt = i; 1165 pcnt = i;
1168 } 1166 }
1169 /* FALL THROUGH */ 1167 /* FALL THROUGH */
1170 /* ------------------------------------------------- */ 1168 /* ------------------------------------------------- */
1171 /* reduce possibilites to "most pieces taken" */ 1169 /* reduce possibilites to "most pieces taken" */
1172 case AVERAGE: 1170 case AVERAGE:
1173 if (pcnt>1) 1171 if (pcnt>1)
1174 { 1172 {
@@ -1179,7 +1177,7 @@ static unsigned char reduceplist(struct move* plist, unsigned char pcnt, unsigne
1179 pcnt = i; 1177 pcnt = i;
1180 } 1178 }
1181 break; 1179 break;
1182 /* ------------------------------------------------- */ 1180 /* ------------------------------------------------- */
1183 default: 1181 default:
1184 // you should never get here! 1182 // you should never get here!
1185 break; 1183 break;
@@ -1353,7 +1351,7 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1353 TEST_PLUGIN_API(api); 1351 TEST_PLUGIN_API(api);
1354 (void)parameter; 1352 (void)parameter;
1355 rb = api; 1353 rb = api;
1356 1354
1357 quit = false; 1355 quit = false;
1358 1356
1359 do /* while !quit */ 1357 do /* while !quit */
diff --git a/apps/plugins/othelo.txt b/apps/plugins/othelo.txt
index bf82e05eb9..169203f32e 100644
--- a/apps/plugins/othelo.txt
+++ b/apps/plugins/othelo.txt
@@ -1,26 +1,19 @@
1Introduction to Othelo 1Introduction to Othelo
2====================== 2======================
3 3
4Othelo is based on a popular board game. This is by no means the first 4Othelo is based on a popular board game. This is by no means the first computer port of this game - the most famous port I can name was "Reversi".
5computer port of this game - the most famous port I can name was "Reversi".
6 5
7The objective is to 'capture' more squares of the board than your opponent. 6The objective is to 'capture' more squares of the board than your opponent.
8 7
9Each turn you claim a single, previously unclaimed, square of the board. But 8Each turn you claim a single, previously unclaimed, square of the board. But EVERY move MUST result in the taking of at least one of your opponents squares.
10EVERY move MUST result in the taking of at least one of your opponents
11squares.
12 9
13An opponents square may be captured by placing your pieces either side of it. 10An opponents square is captured by placing your pieces either side of it. Either vertically, horizontally or diagonally.
14Either vertically, horizontally or diagonally.
15 11
16The rules are mind-numbingly simple ...but, like Chess, the strategy for 12The rules are mind-numbingly simple ...but, like Chess, the strategy for regular success is far from obvious.
17regular success is far from obvious.
18 13
19Further guidance on playing can be found at: 14Further guidance on playing can be found at:
20http://home.nc.rr.com/othello/strategy/midgame/ 15http://home.nc.rr.com/othello/strategy/midgame/
21...although I would like to add that all of the AI Bots (Computer opponents) 16...although I would like to add that all of the AI Bots (Computer opponents) (currently three thereof) were written by yours truly without reference to these type of guides :-)
22(currently three thereof) were written by yours truly without reference to
23these type of guides.
24 17
25 18
26Controls 19Controls
@@ -37,16 +30,15 @@ OFF will switch off the Othelo game. When Othelo is restarted from the menu,
37ON will suspend the current Othelo game. When Othelo is restarted from the 30ON will suspend the current Othelo game. When Othelo is restarted from the
38 menu, play will continue from where it left off. 31 menu, play will continue from where it left off.
39 32
40Either player may be a Human or Computer, and this may be changed at any time 33Either player may be a Human or Computer, and this may be changed at any time during play with F1 and F2. The current choice is indicated by an H or C above the respective F1 and F2 buttons on the Jukebox.
41during play with F1 and F2. The current choice is indicated by an H or C 34
42above the respective F1 and F2 buttons on the Jukebox. 35NOTE: The "suspend" function relied on an 'undocumented' feature which is no longer prevelant under the new "plugin" system. Therefore it needs to be re-implemented as "save progress to file." There are a number of reasons why this has not been implemented - if there is any demand for it, it will be added.
43 36
44 37
45Computer Advice 38Computer Advice
46=============== 39===============
47 40
48When a Human is playing, he may select any of the AI Bots to offer advice (it 41When a Human is playing, he may select any of the AI Bots to offer advice (it is described as the Computer "dreaming" for you).
49is described as the Computer "dreaming" for you).
50 42
51The default advice/help level is 0 (zero) ...no advice 43The default advice/help level is 0 (zero) ...no advice
52Bot 1 help will simply highlight all possible valid moves 44Bot 1 help will simply highlight all possible valid moves
@@ -54,37 +46,25 @@ Bot 2 will advise you which moves would steal the most pieces per turn
54Bot 3 will offer even deeper insights about strategic positioning 46Bot 3 will offer even deeper insights about strategic positioning
55Bot 4 hasn't been written yet ...watch this space 47Bot 4 hasn't been written yet ...watch this space
56 48
57In each case a 'valid' move is displayed as a dot in the centre of the square 49In each case a 'valid' move is displayed as a dot in the centre of the square and a 'choice' move is highlighted with a cross.
58and a 'choice' move is highlighted with a cross.
59 50
60 51
61AI Opponents 52AI Opponents
62=========== 53============
63 54
64There are currently three AI opponents C-1, C-2 and C-3 respectively. Each 55There are currently three AI opponents C-1, C-2 and C-3 respectively. Each has it's own style of play. Losing against one of the opponents does not necessarily mean that you will lose against another.
65has it's own style of play. Losing against one of the opponents does not
66necessarily mean that you will lose against another.
67 56
68It can be interesting to set both opponents to Computer mode and watch them 57It can be interesting to set both opponents to Computer mode and watch them play against each other.
69play against each other.
70 58
71 59
72Navigation 60Navigation
73========== 61==========
74 62
75During the turn of a Human, the up, down, left and right cursor keys navigate 63During the turn of a Human, the up, down, left and right cursor keys navigate the board highlight in a suspiciously expected manner to the chosen square. When the cursor is highlighting the square you wish to play in, press PLAY. If you have chosen an invalid move, the board will flash and you will be expected to try again.
76the board highlight in a suspiciously expected manner the chosen square. When
77the cursor is highlighting the square you wish to play in, press PLAY. If you
78have chosen an invalid move, the board will flash and you will be expected to
79try again.
80 64
81Button repeat _is_ enabled for navigation. 65Button repeat _is_ enabled for navigation.
82 66
83The AI opponents will pause slightly before making their move, to allow you to 67The AI opponents will pause slightly before making their move, to allow you to see the result of your move. If you wish to force the Computer opponent to wait longer before playing, you may keep PLAY depressed ("keep your finger on the piece you just played") and the Computer will make his move immediately you remove your finger from the keypad.
84see the result of your move. If you wish to force the Computer opponent to
85wait longer before playing, you may keep PLAY depressed ("keep your finger on
86the piece you just played") and the Computer will make his move immediately
87you remove your finger from the keypad.
88 68
89 69
90Display 70Display
@@ -92,67 +72,44 @@ Display
92 72
93The main board is that great big grid thing taking up most of the screen. 73The main board is that great big grid thing taking up most of the screen.
94 74
95Above F1 and F2, on the bottom line of the screen, is the identity of each 75Above F1 and F2, on the bottom line of the screen, is the identity of each player. Such as:
96player. Such as:
97"O:C-1" ...this means that, the player represented by the "O" graphic is a 76"O:C-1" ...this means that, the player represented by the "O" graphic is a
98 [C]omputer and is controlled by AI Bot #1 77 [C]omputer and is controlled by AI Bot #1
99"O:H-2" ...this means that, the player represented by the "O" graphic is a 78"O:H-2" ...this means that, the player represented by the "O" graphic is a
100 [H]uman player, and has AI Bot #0 dreaming for him 79 [H]uman player, and has AI Bot #2 dreaming for him
101etc. 80etc.
102 81
103The scores are shown toward the bottom right of the screen and the current 82The scores are shown toward the bottom right of the screen and the current player has a small arrow (->) next to his name, indicating that he should hurry up and actually make a move, today preferably ...although if the music does run out, you can always use the ON button to suspend the game while you go and queue up some more :) ...or not if you are using the current plugin version :(
104player has a small arrow (->) next to his name, indicating that he should
105hurry up and actually make a move, today preferably ...although if the music
106does run out, you can always use the ON button to suspend the game while you
107go and queue up some more :)
108 83
109During play the rest of the screen (the big lump on the right at the top) will 84During play the rest of the screen (the big lump on the right at the top) will show a pointless graphic to make the screen look pretty. When the game is quit or finished naturally, this area will display the identity of the winner, and a secret "end of play" message.
110show a pointless graphic to make the screen look pretty. When the game is
111quit or finished naturally, this area will display the identity of the winner,
112and a secret "end of play" message.
113 85
114 86
115Source Code 87Source Code
116=========== 88===========
117 89
118The source code is scruffy, but well documented. 90The source code is scruffy, but well documented. For the plugin system all headers files were included into the main source code and erased to conform to the new coding standard.
119 91
120 92
121Future Development 93Future Development
122================== 94==================
123 95
124Better AI Bots. A perfect AI Bot is easy and you can add tolerances to it 96Better AI Bots. A perfect AI Bot is easy and you can add tolerances to it from there (to dumb it down a bit) ...the dilemma is the vast quantity of RAM required to run it. So the Bots really need to be strategy and not logic based.
125from there (to dumb it down a bit) ...the dilemma is the vast quantity of RAM
126required to run it. So the Bots really need to be strategy and not logic
127based.
128 97
129Remote control support. Save wear and tear on your Jukebox keys - that really 98Remote control support. Save wear and tear on your Jukebox keys - that really weren't designed with games players in mind.
130weren't designed with games players in mind.
131
132Tidy code to conform to whatever complaints I get about it.
133 99
134 100
135Coding standards, bugs'n'stuff 101Coding standards, bugs'n'stuff
136============================== 102==============================
137 103
138Remember, if you don't like the code ...throw it away and never look at it 104Remember, if you don't like the code ...throw it away and never look at it again! ...Please do not tell me, I don't care. Any complaint requiring a response should be written on the back of notes valued at five thousand shiny new pennies of the British realm. ;)
139again! ...Please do not tell me, I don't care. Any complaint requiring a
140response should be written on the back of notes valued at five thousand shiny
141new pennies of the British realm.
142 105
143On the other hand, I love getting compliments, I am pleased to hear about bugs 106On the other hand, I love getting compliments, I am pleased to hear about bugs I can fix - although they don't tend to happen too often in my code ...go on then, find one! If you can think of any clever short cuts I have missed or fancy your hand at writing the next AI Bot - gimme a shout and I will explain all there is to know about possibility lists, and perhaps even answer such questions as "why is there only one equals sign in this IF statement" and "why are there lines of code with question marks in them"
144I can fix - although they don't tend to happen too often in my code ...go on
145then find one! If you can think of any clever short cuts I have missed or
146fancy your hand at writing the next AI Bot - gimme a shout and I will explain
147all there is to know about possibility lists, and perhaps even answer such
148questions as "why is there only one equals sign in this IF statement" and "why
149are there lines of code with question marks in them"
150 107
151 108
152Greetz 109Greetz
153====== 110======
154 111
155mk, pajaco, Hardeep, DevZer0, jzoss, LinusN 112mk, pajaco, DevZer0, jzoss, LinusN, Zagor, Cyborg, Lord Grumble
156 113
157 114
158Author 115Author
@@ -161,3 +118,5 @@ Author
161BlueChip 118BlueChip
162Yep, the lot - and right chuffed with miself too :) 119Yep, the lot - and right chuffed with miself too :)
163 120
121
122-- othelo.txt - EOF \ No newline at end of file