/* ========================================================================== */
/* */
/* microMax 4.1++ ver 6 — cleaned & fixed for STM32F103C6T6 */
/* (c) 2026 Author: alexlaw@list.ru */
/* Original engine by H. G. Muller */
/* */
/* CHANGES FROM ORIGINAL (marked with FIX: in comments): */
/* FIX 1: Removed HistoryBoards[48][64] — saved 3072 bytes SRAM */
/* FIX 2: Removed unused CopyBoard(), PrintResult(), parse_move() */
/* FIX 3: Removed unused vars: Fifty, UnderProm, Result, HistPtr, etc. */
/* FIX 4: d<98 -> d<20 — prevents stack overflow on 10KB SRAM */
/* FIX 5: Fixed box2 bug: & had higher precedence than || */
/* FIX 6: Wider level range: 5000-11000 middlegame, 18000 endgame */
/* FIX 7: Initialized j=0 in main() — was used as j++ uninitialized */
/* FIX 8: Removed all commented-out dead code */
/* */
/* ========================================================================== */
#include "core.h"
#include "gpio.h"
#include "uart.h"
#include "main.h"
#include "spi.h"
#include "stm32f1xx.h"
#include "string.h"
#include "stdio.h"
#include <stdlib.h>
/* ------------------------------------------------------------------ */
/* GLOBAL VARIABLES */
/* ------------------------------------------------------------------ */
uint32_t TimingDelay;
uint32_t TimingRnd = 0;
uint8_t rc_data[rc_data_size];
uint8_t sd;
uint8_t cnt;
int k = 8;
int *p;
int c[9];
char pos[2];
char mov[6];
char deb[6];
char deb1[6];
char fen[86];
volatile char rx_char;
volatile uint8_t enter;
volatile uint8_t bl = 0;
volatile uint8_t ret = 0;
volatile int level = 13000;
int half_m = 0;
int side;
int one;
/* LED matrix digits 0-9 for MAX7219 (animation during thinking) */
const uint8_t charData[][8] = {
{0x00, 0x1c, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x00}, /* 0 */
{0x08, 0x0c, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00}, /* 1 */
{0x1c, 0x22, 0x20, 0x18, 0x04, 0x02, 0x3e, 0x00}, /* 2 */
{0x1c, 0x22, 0x20, 0x1c, 0x20, 0x22, 0x1c, 0x00}, /* 3 */
{0x20, 0x30, 0x28, 0x24, 0x7c, 0x20, 0x20, 0x00}, /* 4 */
{0x3e, 0x02, 0x1e, 0x20, 0x20, 0x20, 0x1e, 0x00}, /* 5 */
{0x18, 0x04, 0x02, 0x1e, 0x22, 0x22, 0x1c, 0x00}, /* 6 */
{0x3e, 0x20, 0x10, 0x08, 0x04, 0x04, 0x04, 0x00}, /* 7 */
{0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c, 0x00}, /* 8 */
{0x1c, 0x22, 0x22, 0x3c, 0x20, 0x10, 0x0c, 0x00}, /* 9 */
};
/* ------------------------------------------------------------------ */
/* RANDOM OPENING BOOK (BlackBox) */
/* ------------------------------------------------------------------ */
#define BOX_SIZE 5
typedef struct {
int id;
int balls[BOX_SIZE];
} BlackBox;
/* Box1: responses to e2e4 (25668), Box2: responses to d2d4 (25411) */
/* Moves encoded as 256*from + to in 0x88 square notation */
BlackBox box1 = {25668, {4899, 1573, 4658, 5156, 5172}}; /* d7d6 g8f6 c7c5 e7e6 e7e5 */
BlackBox box2 = {25411, {4899, 1573, 4915, 5156, 290}}; /* d7d6 g8f6 d7d5 e7e6 b8c6 */
static BlackBox *selected_box = NULL;
int random_index;
int drawn_ball;
/* ------------------------------------------------------------------ */
/* MICRO-MAX CHESS ENGINE (H. G. Muller) */
/* ------------------------------------------------------------------ */
int StartKey;
#define EMPTY 0
#define WHITE 8
#define BLACK 16
#define PACK_MOVE 256*K + L;
#define UNPACK_MOVE(A) K = (A)>>8 & 255; L = (A) & 255;
/* FIX 1: HistoryBoards removed — was 3072 bytes, never called */
/* FIX 3: Fifty, UnderProm, Result, HistPtr, STATE removed */
#define GH 48
int Side;
int GameHistory[GH];
int GamePtr;
#define W while
#define K(A,B) [I](int[/I])(T+A+(B&8)+S*(B&7))
#define J(A) K(y+A,b[y])-K(x+A,u)-K(H+A,t)
#define U (1<<8)
struct _ {int K,V;char X,Y,D;} A[U]; /* hash table */
int M=136, S=128, I=8e3, C=799, Q, O, K, N, R, J, Z, E1; /* M=0x88 */
char L,
w[]={0,1,1,3,-1,3,5,9}, /* relative piece values */
o[]={-16,-15,-17,0,1,16,0,1,16,15,17,0,14,18,31,33,0,
7,-1,11,6,8,3,6,
6,3,5,7,4,5,3,6}, /* initial piece setup */
b[129], /* board: half of 16x8+dummy */
T[1035], /* hash translation table */
n[]=".?+nkbrq?[I]?NKBRQ"; /[/I] piece symbols on printout */
/* ------------------------------------------------------------------ */
/* D() — recursive minimax search */
/* k=moving side, (q,l)=window, e=eval score, E=e.p. sqr */
/* z=prev.dest, n=depth, J,Z=hashkeys */
/* ------------------------------------------------------------------ */
int D(k,q,l,e,J,Z,E,z,n)
int k,q,l,e,J,Z,E,z,n;
{
int j,r,m,v,d,h,i,F,G,s,f=J,g=Z;
char t,p,u,x,y,X,Y,H,B;
struct _*a=A+(J+k*E&U-1);
q--;
d=a->D; m=a->V; X=a->X; Y=a->Y;
if(a->K-Z|z|
!(m<=q|X&8&&m>=l|X&S))
d=Y=0;
X&=~M;
E1=E;
W(d++<n||
/* FIX 4: d<98 changed to d<20 to prevent stack overflow */
z==8&K==I&&(N<level&d<20||
(K=X,L=Y&~M,d=2)))
{
x=B=X;
h=Y&S;
m=d>1?-I:e;
N++;
do{u=b[x];
if(u&k)
{r=p=u&7;
j=o[p+16];
W(r=p>2&r<0?-r:-o[++j])
{
A:
y=x; F=G=S;
do{
H=y=h?Y^h:y+r;
if(y&M)break;
m=E-S&b[E]&&y-E<2&E-y<2?I:m;
if(p<3&y==E)H^=16;
t=b[H]; if(t&k|p<3&!(y-x&7)-!t)break;
i=99*w[t&7];
m=i<0?I:m;
if(m>=l)goto C;
if(s=d-(y!=z))
{v=p<6?b[x+8]-b[y+8]:0;
b[G]=b[H]=b[x]=0; b[y]=u|32;
if(!(G&M))b[F]=k+6, v+=50;
v-=p-4|R>29?0:20;
if(p<3)
{v-=9*((x-2&M||b[x-2]-u)+
(x+2&M||b[x+2]-u)-1
+(b[x^16]==k+36));
if(y+r+1&S)b[y]|=7, i+=C;
}
J+=J(0); Z+=J(8)+G-S;
v+=e+i; f=m>q?m:q;
v=s>1|v>f?-D(24-k,-l,-f,-v,
J+J(0),Z+J(8)+G-S,F,y,s):v;
if(z&8&&K-I)
{if(v+I&&x==K&y==L)
{Q=-e-i; O=F;
R+=i>>7; return l;
}
v=m;
}
J=f; Z=g;
b[G]=k+6; b[F]=b[y]=0; b[x]=u; b[H]=t;
if(v>m)
m=v, X=x, Y=y|S&F;
if(h){h=0; goto A;}
}
if(x+r-y|u&32|
p>2&(p-4|j-7||
b[G=x+3^r>>1&7]-k-6
||b[G^1]|b[G^2])
)t+=p<5;
else F=y;
}W(!t);
}
}
}W((x=x+9&~M)-B);
C:
if(m>I-M|m<M-I)d=99;
m=m+I?m:-D(24-k,-I,I,0,J,Z,S,S,1);
a->K=Z; a->V=m; a->D=d;
a->X=X|8*(m>q)|S*(m<l); a->Y=Y;
if(z==8)
{
GPIOB->ODR ^= GPIO_ODR_ODR11;
for (int i=1; i<=8; i++)
write_reg(i, charData[cnt][8-i]);
cnt++;
if (cnt>9) cnt=0;
}
}
return m+=m<e;
}
/* ------------------------------------------------------------------ */
/* DELAY FUNCTIONS */
/* ------------------------------------------------------------------ */
void delayMs(int n) {
int i;
for (; n > 0; n--)
for (i = 0; i < 3195; i++) ;
}
void SysTick_Init(void)
{
if (SysTick_Config(SystemCoreClock / 100000))
{
while (1);
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
void Delay_us(uint32_t nTime)
{
TimingDelay = nTime;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while(TimingDelay != 0);
}
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
else
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
/* ------------------------------------------------------------------ */
/* BOARD TO FEN — exports current position as FEN string */
/* Note: castling rights output is a stub ("-") */
/* ------------------------------------------------------------------ */
void board_to_fen() {
char *ptr = fen;
char piece;
int emptyCount = 0;
N=-1; W(++N<121) {
piece = N&8&&(N+=7)?10:n[b[N]&15];
if (ptr >= fen + 86) {*ptr = '\0'; return;}
if (piece == '?') continue;
switch (piece) {
case 10:
if (emptyCount > 0) {
*ptr++ = emptyCount + '0';
emptyCount = 0;
}
if (N < 112) *ptr++ = '/';
break;
case '.':
emptyCount++;
break;
case '+':
if (emptyCount > 0) {
*ptr++ = emptyCount + '0';
emptyCount = 0;
}
*ptr++ = 'p';
break;
case '*':
if (emptyCount > 0) {
*ptr++ = emptyCount + '0';
emptyCount = 0;
}
*ptr++ = 'P';
break;
default:
if (emptyCount > 0) {
*ptr++ = emptyCount + '0';
emptyCount = 0;
}
*ptr++ = piece;
break;
}
}
if (k==8) ptr += sprintf(ptr, " w ");
else ptr += sprintf(ptr, " b ");
ptr += sprintf(ptr, "- ");
if (O != 128) {
*ptr++ = 'a'+(O&7);
*ptr++ = '8'-(O>>4);
*ptr++ = ' ';
} else {
ptr += sprintf(ptr, "- ");
}
ptr += sprintf(ptr, "0 1");
*ptr = '\0';
}
/* ------------------------------------------------------------------ */
/* IS_MOVE — generate all pseudo-legal moves into GameHistory[] */
/* Sets cnt = number of pseudo-legal moves */
/* Uses bit 6 trick on squares 119/112 to force castling detection */
/* ------------------------------------------------------------------ */
void is_move() {
int j, r, V=112;
char t, x, y, u, p, H;
x = 0; cnt = 0;
/* Save old bit 6 values on castling squares */
unsigned char old_bit1 = b[119] & 64;
unsigned char old_bit2 = b[112] & 64;
/* Force virgin bit for castling detection */
b[119] |= 64; b[112] |= 64;
memset(&GameHistory, 0, sizeof(GameHistory));
do {
u = b[x];
if (u & k)
{
r = p = u & 7;
j = o[p+16];
while(r = p>2&r<0?-r:-o[++j])
{
y = x;
do {
H = y += r;
if (y & M) break;
if (p<3 & y==E1) H = y^16;
t = b[H];
if (t&k | p<3 & !(r&7)!=!t) break;
cnt++;
if (GamePtr < GH)
GameHistory[GamePtr++] = 256*x + y;
t += p<5
^ p<3 & 6*k+(y&V)==S
^ j<8 & y
& b[H=x^3^r>>1&7]==u+66
& !b[H^1] & !b[H^2];
} while(!t);
}
}
} while(x = x+9 & ~M);
/* Restore old bit values */
b[119] = (b[119] & ~64) | old_bit1;
b[112] = (b[112] & ~64) | old_bit2;
}
/* ------------------------------------------------------------------ */
/* MAIN */
/* ------------------------------------------------------------------ */
int main(void)
{
CoreInit();
SysTick_Init();
GpioInit();
GPIOA->BSRR = GPIO_BSRR_BR3;
GPIOA->BSRR = GPIO_BSRR_BR4;
GPIOB->BSRR = GPIO_BSRR_BS11;
uart1_init();
SpiInit();
max7219_init();
Delay_us(10000);
max7219_init();
memset(&GameHistory, 0, sizeof(GameHistory));
GamePtr = 0;
half_m = 0;
lab:
ret = 0;
k = 8;
memset(&rc_data, 0, sizeof(rc_data));
memset(&pos, 0, sizeof(pos));
memset(&b, 0, sizeof(b));
uart1_write_String("microMax 4_1++ ver 6");
/* LED self-test: rows then columns */
for (int i = 0; i < 8; i++) {
clear();
set_row(i);
Delay_us(10000);
}
for (int i = 0; i < 8; i++) {
clear();
set_col(i);
Delay_us(10000);
}
clear();
sd = 0;
one = 0;
sprintf(mov, " ");
/* FIX 7: j initialized to 0 — was used as j++ uninitialized */
int j = 0;
K = 8;
int m, nr;
/* FIX 1: HistoryBoards initialization loop removed */
/* Initialize board */
W(K--)
{
b[K] = (b[K+112] = o[K+24]+8) + 8;
b[K+16] = 18;
b[K+96] = 9;
L = 8;
W(L--) b[16*L+K+8] = (int)((K-4)[I](K-4)+(L-3.5)[/I](L-3.5));
}
/* Initialize hash table */
N = 1035;
W(N-->M) T[N] = rand()>>9;
Side = WHITE;
Q = 0;
O = S;
/* Replay pre-loaded opening moves (if any) */
for (nr=0; nr<GamePtr; nr++) {
UNPACK_MOVE(GameHistory[nr]);
D(Side, -I, I, Q, 1, StartKey++, O, 8, 2);
Side ^= 24;
}
if (GamePtr > 0) board_to_fen();
/* ------------------------------------------------------------------ */
/* MAIN GAME LOOP */
/* ------------------------------------------------------------------ */
while(1)
{
GPIOC->BSRR = GPIO_BSRR_BR13;
GPIOA->BSRR = GPIO_BSRR_BS3; /* PA3 — place board indicator */
GPIOA->BSRR = GPIO_BSRR_BS2; /* PA2 */
/* Print board via UART */
N = -1;
W(++N<121)
{
sprintf(pos, "%c", N&8&&(N+=7)?10:n[b[N]&15]);
if (N % 8 == 0)
{
if ((N==16) & (mov[0]!=' ')) {
uart1_write_String(" mov ");
uart1_write_String(mov);
uart1_write_String("@");
}
if (N==32) {
sprintf(deb, " l=%d", level);
uart1_write_String(deb);
}
if (N==48) {
GamePtr = 0;
is_move();
sprintf(deb1, " cnt=%d\n", cnt);
uart1_write_String(deb1);
}
uart1_write_String("\r\n");
}
uart1_write_String(pos);
}
/* Display occupied squares on LED matrix */
{
int i1, j1;
for (int rank = 0; rank < 8; rank++)
{
for (int file = 0; file < 16; file++)
{
int square = rank * 16 + file;
if (file == 0) { i1 = 0; j1 = 0; }
if (!(square & 0x88)) {
if (n[b[square]&15] != '.')
i1 |= 0x01 << j1;
j1++;
}
}
write_reg(8-rank, i1);
}
}
err:
GPIOA->BSRR = GPIO_BSRR_BS2;
memset(&pos, 0, sizeof(pos));
memset(&c, 0, sizeof(c));
p = c;
memset(&rc_data, 0, sizeof(rc_data));
data_count = 0;
NVIC_EnableIRQ(EXTI0_IRQn);
enter = 0;
sprintf(deb, " enter=%d", enter);
uart1_write_String(deb);
Delay_us(300000);
/* Enable SysTick for random seed generation */
bl = 1;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
W(data_count == 0) {
if (ret == 1) break;
/* FIX 5: Corrected operator precedence bug in box condition */
/* Was: (&box1==selected_box)||(&box2==selected_box)&(half_m<2) */
/* Bitwise & binds tighter than ||, so box1 always triggered */
if ((enter==1) | (rc_data[0]=='0'))
{
memset(&rc_data, 0, sizeof(rc_data));
p[0] = '\n';
data_count = -1;
srand(TimingRnd);
/* FIX 6: Wider level range for move variety */
/* Was: 8000+rand()%2001 (always depth 5, same moves) */
/* Now: 5000-11000 (crosses depth 4<->5 boundary) */
if ((half_m > 6) & (R < 29))
level = 5000 + rand() % 6001;
else
level = 18000;
/* FIX 5: Proper parentheses and && operator */
if ((selected_box == &box1 || selected_box == &box2) && half_m < 2)
{
GPIOA->BSRR = GPIO_BSRR_BR3;
NVIC_DisableIRQ(USART1_IRQn);
USART1->CR1 &= ~USART_CR1_RE;
srand(TimingRnd);
random_index = rand() % BOX_SIZE;
drawn_ball = selected_box->balls[random_index];
char xx = (drawn_ball)>>8 & 255;
char yy = drawn_ball & 255;
rc_data[0] = 'a'+(xx&7);
rc_data[1] = '8'-(xx>>4);
rc_data[2] = 'a'+(yy&7);
rc_data[3] = '8'-(yy>>4&7);
data_count = 4;
}
}
}
/* Disable SysTick random mode */
bl = 0;
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
if (ret == 1) goto lab;
delayMs(100);
Delay_us(10000);
uart1_write_String(" GO ");
if (data_count != -1) {
NVIC_DisableIRQ(USART1_IRQn);
USART1->CR1 &= ~USART_CR1_RE;
for (int i = 0; i < data_count; i++) {
if (rc_data[i] == '0') { rc_data[i] = '\n'; }
uart1_write(rc_data[i]);
*p++ = rc_data[i];
}
USART1->CR1 |= USART_CR1_RE;
NVIC_EnableIRQ(USART1_IRQn);
}
NVIC_DisableIRQ(EXTI0_IRQn);
cnt = 0;
K = I; /* invalid move */
if (*c - 10) K = *c - 16*c[1] + C, L = c[2] - 16*c[3] + C;
/* Validate move against pseudo-legal list */
if ((*c - 10) && (GamePtr < GH) && !ret) {
for (int i = 0; i < GamePtr; i++) {
if (GameHistory[i] == (256*K + L)) goto nxt;
}
uart1_write('#');
GPIOA->BSRR = GPIO_BSRR_BR2;
Delay_us(300000);
goto err;
}
nxt:
/* Select opening box based on opponent's first move */
switch (256*K + L) {
case 25668:
selected_box = &box1;
uart1_write('a');
break;
case 25411:
selected_box = &box2;
uart1_write('b');
break;
default:
selected_box = NULL;
break;
}
side = k;
k ^= D(k, -I, I, Q, 1, j++, O, 8, 2) - I ? 0 : 24;
if (side != k) {
half_m++;
char ssd = sd == 0 ? 'w' : 'b';
sprintf(mov, "%c%c%c%c %c",
'a'+(K&7), '8'-(K>>4),
'a'+(L&7), '8'-(L>>4&7), ssd);
sd ^= 1;
clear();
write_reg(atoi(&mov[1]), 0x01 << ((int)mov[0]-97));
if (mov[1] == mov[3]) {
write_reg(atoi(&mov[3]),
(0x01 << ((int)mov[0]-97)) | (0x01 << ((int)mov[2]-97)));
} else {
write_reg(atoi(&mov[3]), 0x01 << ((int)mov[2]-97));
}
} else {
sprintf(mov, " ");
one = 0;
}
GPIOC->BSRR = GPIO_BSRR_BS13;
uart1_write_String(" OK");
Delay_us(300000);
}
}
/* ------------------------------------------------------------------ */
/* SYSTICK HANDLER — dual mode: delay or random seed */
/* ------------------------------------------------------------------ */
void SysTick_Handler(void)
{
if (!bl) { TimingDelay_Decrement(); }
else { TimingRnd++; }
}