Category : A Collection of Games for DOS and Windows
Archive   : BLOCKS.ZIP
Filename : T_TRIS.C

 
Output of file : T_TRIS.C contained in archive : BLOCKS.ZIP
/* T_TRIS version 2.01 adapted by Eric Lapaille
Quai du Condroz 17
B - 4020 LIEGE
BELGIUM
English translation.
Modified for Turbo C 2.0.
Cursor and mouse hiding.
Please, leave this header unmodified

I don't know if the original TETRIS is a commercial product, a public
domain or anyone else. So, if this T_TRIS violates any copyright law
or causes prejudice to some people, please let me know and I'll
immediately remove T_TRIS from the PD market.

If you like this product and use it often, respect the shareware concept
and send to one of us a disk full of great public domain software.

Sorry for spelling errors, but my native tongue is FRENCH. */



/* ---------------------------------------------------------------------- */
/* T_TRIS */
/* Taak Leuk spelen met de gebruiker */
/* */
/* Compiler MicroSoft C 5.0 Large Model */
/* Quick C 1.0 ,, ,, */
/* */
/* (c) 1988 R.A. van Wier */
/* Nwe Prinsengr. 60 II */
/* 1018 VT Amsterdam */
/* */
/* Dit programma is een "kloon" van het tetris programma */
/* van A. Pajitnov & V. Gerasimov. */
/* */
/* Deze software mag door iedereen gebruikt, gecopieerd, */
/* aangepast en verspreid worden onder voorwaarde dat altijd */
/* de juiste source wordt meegeleverd en dat de CopyRight */
/* teksten niet worden verwijderd. */
/* */
/* Houdt FreeWare VIRUS vrij, lever de SOURCE mee ! */
/* */
/* ---------------------------------------------------------------------- */

#include
#include
#include
#include
#include

static int REACTION_TIME,
start_level,
level;
static char name[255];

#define MDA_SEG 0xB000
#define CGA_SEG 0xB800

#define KEY_END 0x011B
#define KEY_RIGHT 0x4D00
#define KEY_LEFT 0x4B00
#define KEY_PAUSE 0x4800
#define KEY_DROP 0x5000
#define KEY_ROTATE 0x1C0D

#define MOUSE_STEP 16

#define SCREEN_BLANKING 0x0700 | ' '

#define CHAR_FULL (unsigned char)219
#define COLOR_BLACK 0
#define COLOR_BROWN 6
#define COLOR_GREY 7
#define COLOR_GREEN 10
#define COLOR_BLUE 11
#define COLOR_RED 12
#define COLOR_VIOLET 13
#define COLOR_YELLOW 14
#define COLOR_WHITE 15
#define COLOR_SCORE 75
#define COLOR_TOPSCORE 30
#define COLOR_LOGO 31

#define COLOR_RAND 3

#define LOG_SIZE_REG 20
#define LOG_SIZE_COL 10

#define MIN_REG 2
#define MAX_REG (LOG_SIZE_REG + MIN_REG)
#define MIN_COL 28
#define MAX_COL (MIN_COL + (LOG_SIZE_COL * 2))

#define NUMBER_FORMS 7
#define FORM_SIZE 3

#define SCORE_FILE "T_TRIS.TOP"
#define NUMBER_SCORE 20

typedef int FORM[FORM_SIZE][FORM_SIZE];

static FORM forms[NUMBER_FORMS];

static FORM huidige_form;

static int form_point[NUMBER_FORMS];

static int log_screen[LOG_SIZE_REG][LOG_SIZE_COL];

static unsigned far *ptr_screen;

static int col;

static long points;
static unsigned key;
static int mouse_present = -1, mouse_col;

/* ----------- position the cursor ------------- */

void cursor(int x, int y)
{
union REGS rg;
rg.x.ax = 0x0200;
rg.x.bx = 0;
rg.x.dx = ((y << 8) & 0xff00) + x;
int86(16, &rg, &rg);
}


/* ---------------------------------------------------------------------- */
/* CHECK_MOUSE */
/* Test if mouse is present */
/* */
/* ---------------------------------------------------------------------- */
static void check_mouse()
{
union REGS regs;

regs.x.ax = 0;
int86(0x33,®s,®s);

if ( regs.x.ax == 0 )
mouse_present = 0;
else
mouse_present = 1;

if ( mouse_present )
{
regs.x.ax = 3;
int86(0x33,®s,®s);
mouse_col = regs.x.cx;
regs.x.ax = 7;
regs.x.cx = MIN_COL * 8;
regs.x.dx = (MAX_COL-1) * 8;
int86(0x33,®s,®s);
}
}

/* ---------------------------------------------------------------------- */
/* MOUSE_CLICK */
/* Check for mouse click */
/* */
/* ---------------------------------------------------------------------- */
static int mouse_click()
{
int retcode;
union REGS regs;
static unsigned vclick = 0;

regs.x.ax = 3;
int86(0x33,®s,®s);

if ( ( regs.x.bx != 0 ) && ( regs.x.bx != vclick ) )
retcode = 1;
else
retcode = 0;

vclick = regs.x.bx;
return(retcode);
}
/* ---------------------------------------------------------------------- */
/* READ_MOUSE */
/* Read mouse move */
/* */
/* ---------------------------------------------------------------------- */
static int read_mouse()
{
int retcode;
union REGS regs;

retcode = 0;

if ( mouse_present < 0 )
check_mouse();

if ( mouse_present )
{
if ( mouse_click() )
{
key = KEY_ROTATE;
retcode = 1;
}
else
{
regs.x.ax = 3;
int86(0x33,®s,®s);
if ( (mouse_col - (int)regs.x.cx) >= MOUSE_STEP )
{
key = KEY_LEFT;
mouse_col -= MOUSE_STEP;
retcode = 1;
}
else
{
if ( ( (int)regs.x.cx - mouse_col) >= MOUSE_STEP )
{
key = KEY_RIGHT;
mouse_col += MOUSE_STEP;
retcode = 1;
}
else
{
regs.x.ax = 11;
int86(0x33,®s,®s);
if ( (int)regs.x.dx >= MOUSE_STEP )
{
key = KEY_DROP;
retcode = 1;
}
}
}
}
}

return(retcode);
}
/* ---------------------------------------------------------------------- */
/* ACTION */
/* Check for reaction time */
/* */
/* ---------------------------------------------------------------------- */
static int action()
{
int retcode;
static long last = 0,
delay;
#if Microsoft
_bios_timeofday(_TIME_GETCLOCK,&delay);
#else
delay=biostime(0,delay);
#endif
if ( ( delay - last ) >= REACTION_TIME )
{
retcode = 1;
last = delay;
}
else
retcode = 0;

return(retcode);
}

/* ---------------------------------------------------------------------- */
/* READ_KEY */
/* Check and read key hitting */
/* */
/* ---------------------------------------------------------------------- */
static int read_key()
{
int retcode;

if ( bioskey(1) )
{
key = bioskey(0);
retcode = 1;
}
else
retcode = read_mouse();

return(retcode);
}

static void wait_key()
{
while( ( !read_key() ) && ( !read_mouse() ) );
}

static void flush_key_buffer()
{
while ( ( read_key() ) || ( read_mouse() ) );
}

/* ---------------------------------------------------------------------- */
/* BEEP */
/* Performs beep */
/* */
/* ---------------------------------------------------------------------- */
static void beep()
{
putch('\x07');
}

/* ---------------------------------------------------------------------- */
/* SHOW_DRAWING */
/* Show the right form in the right color */
/* */
/* ---------------------------------------------------------------------- */
static void show_drawing(line,column,color,drawing)
int line,
column,
color;
unsigned
char drawing;
{
int i;

i = (line * 80) + column;
ptr_screen[i] = (color << 8) | drawing;
}
/* ---------------------------------------------------------------------- */
/* SHOW_STRING */
/* Show the right string at the right place in the right color */
/* */
/* ---------------------------------------------------------------------- */
static void show_string(line,column,color,string)
int line,
column,
color;
unsigned
char string[];
{
int i;

for ( i = 0; string[i]; i++ )
show_drawing(line,column+i,color,string[i]);
}

/* ---------------------------------------------------------------------- */
/* CLS */
/* */
/* ---------------------------------------------------------------------- */
static void cls()
{
int i;

for ( i = 0; i < 2000; i++) ptr_screen[i] = SCREEN_BLANKING;
}

/* ---------------------------------------------------------------------- */
/* INIT_SCREEN */
/* Layout of the screen */
/* */
/* ---------------------------------------------------------------------- */

static void init_screen()
{
int i;
union REGS regs;
char mystring[100];
long delay;

/* Timer and screen initialization */

regs.h.ah = 0x0F;
int86(0x10,®s,®s);

#if Microsoft
_bios_timeofday(_TIME_GETCLOCK,&delay);
#else
delay=biostime(0,delay);
#endif
srand(delay % 32113);


if ( regs.h.al == 7 )
{
#if Microsoft
FP_SEG(ptr_screen) = MDA_SEG;
FP_OFF(ptr_screen) = 0x0000;
#else
ptr_screen = MK_FP(MDA_SEG,0);
#endif
}
else
{
#if Microsoft
FP_SEG(ptr_screen) = CGA_SEG;
FP_OFF(ptr_screen) = 0x0000;
#else
ptr_screen = MK_FP(CGA_SEG,0);
#endif
}

cls();

for ( i = MIN_REG ; i <= MAX_REG; i++ )
{
show_drawing(i,MIN_COL-1,COLOR_RAND,CHAR_FULL);
show_drawing(i,MAX_COL,COLOR_RAND,CHAR_FULL);
}
for ( i = (MIN_COL -1) ; i <= MAX_COL; i++ )
show_drawing(MAX_REG,i,COLOR_RAND,CHAR_FULL);
sprintf(mystring, " T _ T R I S 2.01m %-20.20s (c) 1989, RvW - EL ",name);
show_string(0, 0,COLOR_LOGO,mystring);

check_mouse();

if ( mouse_present )
show_string(24,0,COLOR_LOGO," Mouse/Key : LEFT, DOWN, RIGHT, UP=pause CLICK/RETURN=rotate ESC=end ");
else
show_string(24,0,COLOR_LOGO," Key : cursor LEFT, DOWN, RIGHT UP=pause RETURN=rotate ESC=end ");

}

/* ---------------------------------------------------------------------- */
/* SHOW_SCORE */
/* Show score and level */
/* */
/* ---------------------------------------------------------------------- */

static void show_score()
{
char mystring[80];

show_string(10,0,COLOR_SCORE," ");
sprintf(mystring," Level %1d / %1d ",start_level,level);
show_string(11,0,COLOR_SCORE,mystring);
show_string(12,0,COLOR_SCORE," ");
sprintf(mystring," Score %5ld ",points);
show_string(13,0,COLOR_SCORE,mystring);
show_string(14,0,COLOR_SCORE," ");
}

/* ---------------------------------------------------------------------- */
/* SHOW_TOPSCORE */
/* Show one topscore by player */
/* */
/* ---------------------------------------------------------------------- */

static void show_topscore()
{
int i,
j;
char mystring[80];
long mystring_score,
mystring_gem,
mystring_gesp;
static struct
{
int number;
long score[NUMBER_SCORE];
long gem[NUMBER_SCORE];
long gesp[NUMBER_SCORE];
char name[NUMBER_SCORE][22];
} score_record;
FILE *score_filename;

/* Read old scores */

score_filename = fopen(SCORE_FILE,"rb");
if ( score_filename == NULL )
score_record.number = 0;
else
{
if ( fread(&score_record,sizeof(score_record),1,score_filename) < 1)
score_record.number = 0;
fclose(score_filename);
}

/* Look for player in the list */

name[21] = '\0';
strupr(name);
j = 99;
for ( i = 0; i < score_record.number; i++ )
{
if ( strcmp(name,score_record.name[i]) == 0 )
j = i;
}
if ( j < 99 )
{
/* Compute score and average */

if ( points > score_record.score[j] )
score_record.score[j] = points;
score_record.gem[j] = ( ( score_record.gem[j] * score_record.gesp[j] )
+ points ) / ( score_record.gesp[j] + 1);
score_record.gesp[j] = score_record.gesp[j] + 1;
}
else
{
if ( score_record.number < NUMBER_SCORE )
{
score_record.score[score_record.number] = points;
score_record.gem[score_record.number] = points;
score_record.gesp[score_record.number] = 1;
strcpy(score_record.name[score_record.number],name);
j = score_record.number;
score_record.number++;
}
else
{
if ( points > score_record.score[NUMBER_SCORE - 1] )
{
/* Remove last score and replace it my new score */

score_record.score[NUMBER_SCORE - 1] = points;
score_record.gem[NUMBER_SCORE - 1] = points;
score_record.gesp[NUMBER_SCORE - 1] = 1;
strcpy(score_record.name[NUMBER_SCORE - 1],name);
j = NUMBER_SCORE - 1;
}
}
}

if ( j < 99 )
{
while ( ( j > 0 ) && ( score_record.score[j] > score_record.score[j-1] ) )
{
strcpy(mystring,score_record.name[j-1]);
mystring_score = score_record.score[j-1];
mystring_gem = score_record.gem[j-1];
mystring_gesp = score_record.gesp[j-1];

strcpy(score_record.name[j-1],score_record.name[j]);
score_record.score[j-1] = score_record.score[j];
score_record.gem[j-1] = score_record.gem[j];
score_record.gesp[j-1] = score_record.gesp[j];

strcpy(score_record.name[j],mystring);
score_record.score[j] = mystring_score;
score_record.gem[j] = mystring_gem;
score_record.gesp[j] = mystring_gesp;

j--;
}
}

score_filename = fopen(SCORE_FILE,"wb");
if ( score_filename != NULL )
{
fwrite(&score_record,sizeof(score_record),1,score_filename);
fclose(score_filename);
}

/* Show score list on the screen */

for (i = 0; i < LOG_SIZE_REG; i++)
{
if ( i < score_record.number )
sprintf(mystring," Top %5ld, Avg. %5ld pnt. for %-16.16s ",
score_record.score[i],
score_record.gem[i],score_record.name[i]);
else
strcpy(mystring," ");
show_string(MIN_REG+i,MIN_COL,COLOR_TOPSCORE,mystring);
}
}

/* ---------------------------------------------------------------------- */
/* LOG_DISPLAY */
/* */
/* ---------------------------------------------------------------------- */

static void log_display()
{
int i,j;

for ( i = 0; i < LOG_SIZE_REG; i++ )
{
for ( j = 0; j < LOG_SIZE_COL; j++ )
{
show_drawing(MIN_REG+i,MIN_COL+(j*2),log_screen[i][j],CHAR_FULL);
show_drawing(MIN_REG+i,MIN_COL+1+(j*2),log_screen[i][j],CHAR_FULL);
}
}
}

/* ---------------------------------------------------------------------- */
/* LOG_CLS */
/* */
/* ---------------------------------------------------------------------- */
static void log_cls()
{
int i,j;

for ( i = 0 ; i < LOG_SIZE_REG; i++)
{
for ( j = 0; j < LOG_SIZE_COL; j++)
log_screen[i][j] = COLOR_BLACK;
}
log_display();
check_mouse();
}

static void remove_full_line()
{
int i,
j;
int x,
action;

for ( i = LOG_SIZE_REG - 1 ; i > 0; i--)
{
/* Look for a gap on the line */

action = 1;
for ( j = 0; j < LOG_SIZE_COL; j++)
{
if (log_screen[i][j] == COLOR_BLACK)
action = 0;
}
if ( action )
{
for ( x = i ; x > 0; x--)
{
for ( j = 0; j < LOG_SIZE_COL; j++)
log_screen[x][j] = log_screen[x-1][j];
}
for ( j = 0; j < LOG_SIZE_COL; j++)
log_screen[0][j] = COLOR_BLACK;
beep();
i++;
}
}
log_display();
}

/* ---------------------------------------------------------------------- */
/* SHOW_FORM */
/* Form manipulation */
/* ---------------------------------------------------------------------- */
static int show_form(action)
int action; /* 0 = init */
/* 1 = down */
/* 2 = right */
/* 3 = left */
/* 5 = rotate */
{
int i,j;
static int reg,col;
int mystring,change;

change = 1;

if ( action != 0 )
{
/* Blank old position */

for ( i = 0; i < 3; i++ )
{
for ( j = 0; j < 3; j++ )
{
if ( huidige_form[i][j] != COLOR_BLACK )
log_screen[reg+i][col+j] = COLOR_BLACK;
}
}
}

switch(action)
{
case 0 :
reg = 0;
col = LOG_SIZE_COL / 2;
check_mouse();
break;
case 1 :
reg++;
break;
case 2 :
col++;
break;
case 3 :
col--;
break;
case 5 :
mystring = huidige_form[0][0];
huidige_form[0][0] = huidige_form[2][0];
huidige_form[2][0] = huidige_form[2][2];
huidige_form[2][2] = huidige_form[0][2];
huidige_form[0][2] = mystring;
mystring = huidige_form[0][1];
huidige_form[0][1] = huidige_form[1][0];
huidige_form[1][0] = huidige_form[2][1];
huidige_form[2][1] = huidige_form[1][2];
huidige_form[1][2] = mystring;
break;
}

for ( i = 0; i < 3; i++ )
{
for ( j = 0; j < 3; j++ )
{
if ( huidige_form[i][j] != COLOR_BLACK )
{
if ( log_screen[reg+i][col+j] != COLOR_BLACK )
change = 0;
if ( ( reg+i ) >= LOG_SIZE_REG )
change = 0;
if ( ( reg+i ) < 0 )
change = 0;
if ( ( col+j ) >= LOG_SIZE_COL )
change = 0;
if ( ( col+j ) < 0 )
change = 0;
}
}
}

if ( !change )
{
switch(action)
{
case 1 :
reg--;
break;
case 2 :
col--;
break;
case 3 :
col++;
break;
case 5 :
mystring = huidige_form[0][0];
huidige_form[0][0] = huidige_form[0][2];
huidige_form[0][2] = huidige_form[2][2];
huidige_form[2][2] = huidige_form[2][0];
huidige_form[2][0] = mystring;
mystring = huidige_form[0][1];
huidige_form[0][1] = huidige_form[1][2];
huidige_form[1][2] = huidige_form[2][1];
huidige_form[2][1] = huidige_form[1][0];
huidige_form[1][0] = mystring;
break;
}
}

/* form at new position */

for ( i = 0; i < 3; i++ )
{
for ( j = 0; j < 3; j++ )
{
if ( huidige_form[i][j] != COLOR_BLACK )
log_screen[reg+i][col+j] = huidige_form[i][j];
}
}

log_display();

return(change);
}

/* ---------------------------------------------------------------------- */
/* INIT_FORMS */
/* */
/* ---------------------------------------------------------------------- */
static void init_forms()
{
int i,
j;
int color;

form_point[0] = 10;
color = COLOR_BROWN;
forms[0][0][0] = COLOR_BLACK; /* ..# */
forms[0][0][1] = COLOR_BLACK; /* .## */
forms[0][0][2] = color; /* .#. */

forms[0][1][0] = COLOR_BLACK;
forms[0][1][1] = color;
forms[0][1][2] = color;

forms[0][2][0] = COLOR_BLACK;
forms[0][2][1] = color;
forms[0][2][2] = COLOR_BLACK;

form_point[1] = 10;
color = COLOR_BLUE;
forms[1][0][0] = color ; /* #.. */
forms[1][0][1] = COLOR_BLACK; /* ##. */
forms[1][0][2] = COLOR_BLACK; /* .#. */

forms[1][1][0] = color ;
forms[1][1][1] = color ;
forms[1][1][2] = COLOR_BLACK;

forms[1][2][0] = COLOR_BLACK;
forms[1][2][1] = color ;
forms[1][2][2] = COLOR_BLACK;

form_point[2] = 1;
color = COLOR_RED;
forms[2][0][0] = COLOR_BLACK; /* ... */
forms[2][0][1] = COLOR_BLACK; /* ### */
forms[2][0][2] = COLOR_BLACK; /* ... */

forms[2][1][0] = color ;
forms[2][1][1] = color ;
forms[2][1][2] = color ;

forms[2][2][0] = COLOR_BLACK;
forms[2][2][1] = COLOR_BLACK;
forms[2][2][2] = COLOR_BLACK;

form_point[3] = 7;
color = COLOR_VIOLET;
forms[3][0][0] = COLOR_BLACK; /* ... */
forms[3][0][1] = COLOR_BLACK; /* ### */
forms[3][0][2] = COLOR_BLACK; /* ..# */

forms[3][1][0] = color ;
forms[3][1][1] = color ;
forms[3][1][2] = color ;

forms[3][2][0] = COLOR_BLACK;
forms[3][2][1] = COLOR_BLACK;
forms[3][2][2] = color ;

form_point[4] = 7;
color = COLOR_YELLOW;
forms[4][0][0] = COLOR_BLACK; /* ... */
forms[4][0][1] = COLOR_BLACK; /* ### */
forms[4][0][2] = COLOR_BLACK; /* #.. */

forms[4][1][0] = color ;
forms[4][1][1] = color ;
forms[4][1][2] = color ;

forms[4][2][0] = color ;
forms[4][2][1] = COLOR_BLACK;
forms[4][2][2] = COLOR_BLACK;

form_point[5] = 4;
color = COLOR_GREY;
forms[5][0][0] = COLOR_BLACK; /* ... */
forms[5][0][1] = COLOR_BLACK; /* ##. */
forms[5][0][2] = COLOR_BLACK; /* ##. */

forms[5][1][0] = color ;
forms[5][1][1] = color ;
forms[5][1][2] = COLOR_BLACK;

forms[5][2][0] = color ;
forms[5][2][1] = color ;
forms[5][2][2] = COLOR_BLACK;

form_point[6] = 3;
color = COLOR_GREEN;
forms[6][0][0] = COLOR_BLACK; /* ... */
forms[6][0][1] = COLOR_BLACK; /* .#. */
forms[6][0][2] = COLOR_BLACK; /* ### */

forms[6][1][0] = COLOR_BLACK;
forms[6][1][1] = color ;
forms[6][1][2] = COLOR_BLACK;

forms[6][2][0] = color ;
forms[6][2][1] = color ;
forms[6][2][2] = color;

}

/* ---------------------------------------------------------------------- */
/* CHOOSE_FORM */
/* Random choose of the form */
/* */
/* ---------------------------------------------------------------------- */

static void choose_form()
{
int i,j,x;
static int tel = 0;
static long vpunten = 0;

if ( vpunten > points )
{
vpunten = 0;
tel = 0;
}

x = rand() % NUMBER_FORMS;

for ( i = 0; i < 3 ; i++)
{
for ( j = 0; j < 3; j++ )
huidige_form[i][j] = forms[x][i][j];
}
points += form_point[x] + start_level;

tel++;

if ( ( ( tel % 25 ) == 0 ) && ( level < 9 ) )
{
level++;
beep();
}

show_score();

vpunten = points;
REACTION_TIME = 10 - level;
}

/* ------------------------------------------------------------------------ */
/* */
/* MAIN */
/* */
/* ------------------------------------------------------------------------ */

void main()
{
int i;
char strlevel[3];

key = 0;

while ( key != KEY_END )
{

level = 99;

strcpy(name,"");
while ( name[0] == '\0' )
{
clrscr();
printf("\nWhat is your name > ");
gets(name);
}
while ( ( level < 0 ) || ( level > 9 ) )
{
printf("\nDear %s, choose your level ( 0..9) > ",name);
gets(strlevel);
level=atoi(strlevel);
}
cursor(0,26);
start_level = level;
points = 0;
init_screen();
log_cls();
init_forms();
choose_form();

key = 0;

while ( ( key != KEY_END ) && ( show_form(0) ) )
{
while ( ( key != KEY_END ) && ( show_form(1) ) )
{
action();

while ( ( !action() ) && ( key != KEY_END ) )
{
if ( read_key() )
{
switch(key)
{
case KEY_RIGHT :
show_form(2);
break;
case KEY_LEFT :
show_form(3);
break;
case KEY_DROP :
i = 0;
while( show_form(1)) i++;
points = points + start_level +
((i / LOG_SIZE_REG) * start_level);
break;
case KEY_PAUSE :
wait_key();
break;
case KEY_ROTATE :
show_form(5);
break;
}
}
}
}
remove_full_line();
flush_key_buffer();
choose_form();
}

if ( key != KEY_END )
{
show_topscore();
flush_key_buffer();
wait_key();
}

cls();
}
}


  3 Responses to “Category : A Collection of Games for DOS and Windows
Archive   : BLOCKS.ZIP
Filename : T_TRIS.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/