Guessing game where the first player inputs a word and a hint and the second player tries to guess it
You have a bit of code duplication that could be extracted and consolidated. But first with some error fixes (at least the code did not compile on my TASM).

Compilation issues

right:
    lea dx [win] ; &lt;-- missing comma between dx and [win]


fail is defined twice. First as a string (fail db ' Enter guess again($') and the second time as a label. I would rename the first one to failmsg and use it in

lea dx, [failmsg] ; &lt;-- instead of fail before


With those fixes the code compiles now. As for the code review I would start with

DOS functions

mov ah,09
int 21h


which is just Display string. So let's call it like that and make sure a requirement that when calling it we have dx

PROC display_string
   mov ah,09
   int 21h
   ret
ENDP


and instead of

lea dx, [a3msg]
mov ah, 9
int 21h


just do

lea dx, [a3msg]
call display_string


The same goes for other DOS functions

PROC get_char
   mov ah,01
   int 21h
   ret
ENDP

PROC put_char
   mov ah,02
   int 21h
   ret
ENDP


Using those instead of bare int-ing will make your code more readable. Having those it's clear what you try to do with this code

High-level functions

mov dx, 10
call put_char


but in DOS it should be 2 characters: 13,10 so it's better to do like this:

PROC new_line
  mov dx,13
  call put_char
  mov dx,10
  call put_char
  ret
ENDP


Buffered input

I don't know why you chose buffered input as your method of entering the hint, but in case of this command the data has a specific format.

hint db 100,?,101 dup(0)


The actual string starts on index 2 and before that you have max string length, and bytes read. Also there's no $ at the end. You have to put it there.

xor bx,bx
mov bl, [hint+1]
mov [hint+bx+2], '$'


So printing the hint should look like this:

lea dx, [hint+2]
call display_string


Printing the count

This is obviously wrong

lea dx, [cnt]
call put_char


as in the cnt will be the number of characters not the ascii representation of that number. If we could restrict ourselves to maximum 9 characters that would be easy. Just add 48(dec) to the cnt and you get the value. Since your secret is limited to 21 characters we could go with a simple method for printing.

PROC print_number
    push bx
    push dx
    mov bx,10
    xor dx,dx
    div bx
    mov bx,dx
    cmp ax,0
    je second
    mov dx,ax
    add dx,48
    call put_char
second:
    mov dx,bx
    add dx,48
    call put_char
    pop dx
    pop bx
    ret
ENDP


What we do here is just divide the number in ax by 10 and print the values stored in ax (result - only if >0) and the dx - reminder.

Printing the hints

On fourth attempt you are putting the character to be printed in dh. It should be in dl. You don't also have to use different registers so just:

mov dl, [secret]
call put_char
mov dl, [secret+1]
call put_char


To print the last char you need to keep in might that the last is '$' so just one before the last

mov di, [cnt]
mov dl, [secret+di-1]
call put_char


The same issue when you try to print the 6th hint. Also that you did not retrieve the char at all.

mov di, [cnt]
sub di,2
mov dl, [secret+di-1]
call put_char


Compare

Your compare method is kinda strange. You do not iterate at all and you don't use any opcodes that would do that for you. It works only when you have one-char secret. Not so cool :)

I would write it like this

PROC compare
  push cx
  lea si, [secret]
  lea di, [guess]
  rep cmpsb
  pop cx
  ret
ENDP


and change how to check for equality

call compare
je right


Other things that might be good to fix

You have the same loop for scanning the characters. Why not enclose it as another proc and just use in those two places? You can make assumption that for example di will point to the buffer that has to be filled. Also in this scanning you could handle for example backspace character and delete the characters.

PROC read_input
  push cx
  mov bx, 0
  mov cx, 0
scan:
  call get_char
  cmp al, '.'
  je com
  inc cx
  mov [di+bx], al
  inc bx
  jmp scan
com:
  mov [di+bx], '$'
  mov ax,cx
  pop cx
  ret
ENDP


And use it

lea di,[secret]
call read_input
mov [cnt],ax


and similarly in the second place.

Also some checks for input length might be good to do.

You should also comment more your code. I strongly believe the code should be self documented but not on such low level as asm :) So comment more.

GIST