Blackjack - A Solution to 2005 Project Two



 
	; CS 130 Project Two - Fall, 2005 
	; Blackjack problem from 2005 ACM Regional Programming Contest

	INCLUDE Irvine16.inc

	.DATA;

	IntArray 	WORD 1000 DUP (?)

	Greeting	BYTE "Welcome to Las Vegas Blackjack Game." , 0
	Prompt		BYTE "Enter the array of integers (ending with 0)." , 0
	ErrMessage	BYTE "ERROR: integer out of allowable range." , 0
	DeckMessage	BYTE "Deck number " , 0 
	WonMessage	BYTE "Number of games won: " , 0
	LostMessage	BYTE "Number of games lost: " , 0 
	TiedMessage	BYTE "Number of games tied: " , 0
	OptCaseMes	BYTE "OptCase = " , 0
	OptExtraMes	BYTE "OptExtra = " , 0
	OptDealtMes	BYTE "OptDealt = " , 0
	
	DeckCounter	WORD 0 
	HouseScore	WORD ?
	PlayerScore	WORD ?
	HouseHasAce	WORD ?
	PlayerHasAce	WORD ?
	DeckSize	WORD ?
	FirstCardIndex	WORD ?
	TooFarIndex	WORD ?
	OptExtra	WORD ? 
	OptCase		WORD ? 
	OptDealt	WORD ? 
	NumSkip		WORD ?
	NumExtra	WORD ?
	NumDealt	WORD ?
	NumWon		WORD ? 
	NumLost		WORD ? 
	NumTied		WORD ? 
	

	.CODE
MAIN 		PROC

	mov ax, @data
	mov ds, ax     
	call CrLf 
        mov dx, OFFSET Greeting
	call WriteString
	call CrLf
	call CrLf	
	call ReadArray
	call CheckArray
	call ProcessArray
	exit
	
MAIN 		ENDP
     	
     	
ReadArray 	PROC

; Load IntArray with user input until receiving a zero.

	push dx
	push ax
	push si
	mov dx, OFFSET Prompt
	call WriteString
	call CrLf
	call CrLf
	mov si, 0 
    L1: call ReadInt
	call CrLf
	mov IntArray[si], ax
	add si, 2
	cmp ax,0
	jne L1
	pop si
	pop ax
	pop dx
	ret

ReadArray	ENDP

CheckArray	PROC

; Check that data entered agrees with specs in project. 
; Terminate program if not. 

	push si
	mov si, 0
    L1: cmp IntArray[si], 0
	je quit
	cmp IntArray[si], 500
	ja err
	mov cx,IntArray[si]
	add si, 2
    L2: cmp IntArray[si],0
        je err
        cmp IntArray[si],10
        ja err
        add si, 2
  	loop L2
  	jmp L1  	
  quit: pop si
      	ret
  err:  mov dx, OFFSET ErrMessage
      	call WriteString
      	call CrLf
       	exit
        
CheckArray	ENDP

ProcessArray	PROC 

; Process all data in the array. Simulate multiple Blackjack games 
; using multiple decks. 

	push si 
	push ax

	mov si, 0 
  lewp: mov ax, IntArray[si]	; get next deck size 
  	cmp ax, 0 		; quit if zero 
  	je done 
  	call PlayDeck 		; process a whole deck 
  	inc ax
  	shl ax, 1 
  	add si, ax 		; skip ahead to next deck 
  	jmp lewp 		; loop back 
  	
  done: pop ax 
  	pop si 
  	ret   	

ProcessArray	ENDP 

PlayDeck	PROC 

; Simulate playing several hands of Blackjack using same deck, 
; until all cards have been used. Display results. 
; Pre:
; SI = index for start of current deck 
; Post:
; NumWon = # player wins 
; NumLost = # player losses 
; NumTied = # ties 
; increment DeckCounter 

	push ax
	push dx 
	mov NumWon, 0 
	mov NumLost, 0 
	mov NumTied, 0 
	mov NumSkip, 0 
	
  lewp: call FindBestPlay	; play a hand  
  	cmp OptCase, 0		; increment one of 3 counters
  	jl loss
  	jg win 
  	inc NumTied
  	jmp skip 
  loss: inc NumLost 
  	jmp skip 
  win:  inc NumWon 

  skip:  
  	mov ax, numSkip 
  	add ax, optDealt 
  	cmp ax, IntArray[si]	; IntArray[si] is deck size  
  	jge done 		; quit when whole deck has been used 

  	mov NumSkip, ax		; otherwise loop back to play again, 
	jmp lewp		; dealing after last card previously dealt 
	
  done: 
  	inc DeckCounter 
  	call CrLf 		; report tallies 
  	mov dx, OFFSET DeckMessage 
  	call WriteString 
  	movsx eax, DeckCounter 
  	call WriteInt 
  	call CrLf 
  	mov dx, OFFSET WonMessage 
  	call WriteString 
  	movsx eax, NumWon 
  	call WriteInt 
  	call CrLf 
  	mov dx, OFFSET LostMessage 
  	call WriteString 
  	movsx eax, NumLost
  	call WriteInt 
  	call CrLf 
  	mov dx, OFFSET TiedMessage 
  	call WriteString 
  	movsx eax, NumTied 
  	call WriteInt 
  	call CrLf 

  	pop dx
  	pop ax 
  	ret

PlayDeck	ENDP 

FindBestPlay	PROC

; Determine least number of extra cards player can draw in order 
; to obtain best possible outcome (win, tie or loss). 
; Pre: 
; SI = index for start of current deck 
; NumSkip = # cards to skip in deck before dealing 
; Post: 
; OptExtra = optimal # extra cards for player 
; OptDealt = # cards dealt 
; OptCase = -1 (loss), 0 (tie) or 1 (win) 

	push ax
	push bx 
	mov NumExtra, 0 
	mov OptCase, -2 
  
  lewp: call PlayHand 
  	cmp ax, OptCase 
  	jle skip 		; skip ahead unless we got better outcome 
  
  	mov OptCase, ax 	; record new optimal data 
  	mov bx, NumExtra
  	mov OptExtra, bx 
  	mov bx, NumDealt 
  	mov OptDealt, bx 
  	cmp ax, 1
  	je done			; quit if win was discovered


  skip: cmp PlayerScore, 21 
  	ja done			; quit if player went bust last time (cannot ...
  				; take extra cards after busting, right?) 
; OOPS. BAD IDEA: 
;  	mov bx, NumExtra 
;	inc bx
;	cmp bx, IntArray[si] 
;	jae done		; quit when # extra cards becomes silly
  	
  	inc NumExtra
  	jmp lewp		; otherwise, try again, taking more cards
  	
  done:	
  	push dx 		; diagnostic stuff
  	call CrLf
	mov dx, OFFSET OptCaseMes
	call WriteString 
	movsx eax, OptCase 
	call WriteInt 
	call CrLf 
	mov dx, OFFSET OptExtraMes
	call WriteString 
	movsx eax, OptExtra
	call WriteInt 
	call CrLf 
	mov dx, OFFSET OptDealtMes
	call WriteString 
	movsx eax, OptDealt 
	call WriteInt 
	call CrLf 
	
  	pop dx 
  
  	pop bx
  	pop ax
  	ret

FindBestPlay	ENDP 

PlayHand	PROC 

; Simulate playing a hand of Blackjack using a given deck, starting to deal 
; from a particular place in the deck, and having player take a specified 
; number of extra cards. 
;Pre:
;SI = index of the start of the deck (where # cards in deck is stored) 
;NumSkip = number of cards to skip before dealing 
;NumExtra = number of extra cards to be dealt to the player 
;Post:
;ax = 1 (win for player), -1 (loss for player) or 0 (tie)

	push cx
	push si
	mov NumDealt, 0 
	mov HouseHasAce,0
	mov PlayerHasAce,0
	mov HouseScore, 0
	mov PlayerScore, 0
	mov ax, IntArray[si]
	mov DeckSize, ax	; how many cards in deck? 
	add si, 2
	mov FirstCardIndex, si  ; mark location of 1st card in deck 
	shl ax,1
	add ax, si
	mov TooFarIndex, ax 	; mark location just beyond current deck 
	mov ax, NumSkip
	shl ax, 1
	add si, ax		; si now points to first card to be dealt 
			
	call DealPlayer		; now deal first four cards
	call DealHouse
	call DealPlayer
	call DealHouse
	
	mov cx, NumExtra	; now deal extra cards to player 
	cmp cx, 0
	je houseLp
playrLp:call DealPlayer
        loop playrLp
				; now deal cards to house until 17
houseLp:mov ax,HouseHasAce	; skip ahead if house has an ace 
        cmp HouseHasAce, 1
        je hasAce
	cmp HouseScore, 17
        jae stopDeal		; done dealing if 17 or over 
        jmp needMore		; haven't reached 17 yet, so deal
hasAce: cmp HouseScore, 17	
        jae stopDeal		; done dealing if 17 or over, else  
        cmp HouseScore, 12
        jae needMore		; deal again if 12 to 16, else  
        cmp HouseScore, 7
        jae stopDeal		; done dealing if 7 to 11 (because of ace),
				; else deal again if 1 to 6 
needMore:call DealHouse
        jmp houseLp
        
StopDeal:
        cmp PlayerScore, 11	; if player's score is under 12...
        ja skip1
        cmp PlayerHasAce, 1	; and if player holds an ace, then...
        jne skip1 
        add PlayerScore, 10	; add 10 to player's score
        
 skip1: cmp HouseScore, 11	; if house's score is under 12...
      	ja decision
      	cmp HouseHasAce, 1	; and if house holds an ace, then...
      	jne decision
    	add HouseScore, 10	; add 10 to house's score
  
decision: 
      	cmp PlayerScore, 21
      	jbe skip2
        cmp HouseScore, 21
        ja tie			; tie if both went bust
        jmp loss		; loss if only player went bust 
 skip2: cmp HouseScore, 21
        jbe skip3
        jmp win			; win if only house went bust 
 skip3: mov ax, PlayerScore
        cmp ax, HouseScore	; if neither went bust, compare scores
        je tie
        jb loss        
        
  win:	mov ax, 1
    	jmp done
  loss: mov ax, -1
    	jmp done    
  tie:  mov ax, 0      
  done: pop si
        pop cx    	
        ret
        
PlayHand	ENDP

DealPlayer	PROC

; Simulate dealing a card to player. 
; Pre:
; SI = index of next card to be dealt 
; Post:
; PlayerScore will be updated.
; Checks for ace and sets PlayerHasAce flag if so.
; Advance SI and check for wrap around.
; Increment NumDealt.

	push ax
	mov ax, IntArray[si]
	add PlayerScore, ax
	cmp ax, 1
	jne S1
	mov PlayerHasAce, 1
    S1: add si, 2
      	cmp si, TooFarIndex
      	jne S2
      	mov si, FirstCardIndex        
    S2: inc NumDealt
      	pop ax
      	ret

DealPlayer 	ENDP

DealHouse 	PROC

; Simulate dealing a card to house. 
; Pre:
; SI = index of next card to be dealt 
; Post:
; HouseScore will be updated.
; Checks for ace and sets HouseHasAce flag if so.
; Advance SI and check for wrap around.
; Increment NumDealt.

	push ax
	mov ax, IntArray[si]
	add HouseScore, ax
	cmp ax, 1
	jne S1
	mov HouseHasAce, 1
    S1: add si, 2
      	cmp si, TooFarIndex
      	jne S2
      	mov si, FirstCardIndex
    S2:	inc NumDealt
      	pop ax
      	ret

DealHouse 	ENDP 

	END 	MAIN	

Here is what happened when I ran it with the test data:

 

C:\Masm615\Projects\proj2\mine>bj

Welcome to Las Vegas Blackjack Game.

Enter the array of integers (ending with 0).

10
1
2
3
4
5
6
7
8
9
10
13
8
4
9
1
4
9
2
10
7
3
1
4
4
20
4
6
3
7
10
1
5
5
6
10
1
8
4
4
10
9
2
2
1
5
0

OptCase = +0
OptExtra = +1
OptDealt = +7

OptCase = +0
OptExtra = +1
OptDealt = +5

Deck number +1
Number of games won: +0
Number of games lost: +0
Number of games tied: +2

OptCase = +1
OptExtra = +1
OptDealt = +8

OptCase = +0
OptExtra = +2
OptDealt = +8

Deck number +2
Number of games won: +1
Number of games lost: +0
Number of games tied: +1

OptCase = +1
OptExtra = +0
OptDealt = +5

OptCase = -1
OptExtra = +0
OptDealt = +5

OptCase = +1
OptExtra = +0
OptDealt = +5

OptCase = +1
OptExtra = +2
OptDealt = +7

Deck number +3
Number of games won: +3
Number of games lost: +1
Number of games tied: +0