;15 PUZZLE GAME
;Ram Variables
Cursor_X equ $00FF0000		;Ram for Cursor Xpos
Cursor_Y equ $00FF0000+1	;Ram for Cursor Ypos
joypad1 equ $00FF0002

GameRam equ $00FF1000		;Ram for where the pieces are
GameRam_End equ $00FF100F	;the last valid slot in the array
;Video Ports
VDP_data	EQU	$C00000	; VDP data, R/W word or longword access only
VDP_ctrl	EQU	$C00004	; VDP control, word or longword writes only

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 					VECTOR TABLE
;org $00000000
	DC.L	$00FFFFFE		;SP register value
	DC.L	ProgramStart	;Start of Program Code
	DC.L	IntReturn		; bus err
	DC.L	IntReturn		; addr err
	DC.L	IntReturn		; illegal inst
	DC.L	IntReturn		; divzero
	DC.L	IntReturn		; CHK
	DC.L	IntReturn		; TRAPV
	DC.L	IntReturn		; privilege viol
	DC.L	IntReturn		; TRACE
	DC.L	IntReturn		; Line A (1010) emulator
	DC.L	IntReturn		; Line F (1111) emulator
	DC.L	IntReturn,IntReturn,IntReturn,IntReturn		; Reserved /Coprocessor/Format err/ Uninit Interrupt
	DC.L	IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn
	DC.L	IntReturn		; spurious interrupt
	DC.L	IntReturn		; IRQ level 1
	DC.L	IntReturn		; IRQ level 2 EXT
	DC.L	IntReturn		; IRQ level 3
	DC.L	IntReturn		; IRQ level 4 Hsync
	DC.L	IntReturn		; IRQ level 5
	DC.L	IntReturn		; IRQ level 6 Vsync
	DC.L	IntReturn		; IRQ level 7 (NMI)
;org $00000080
;TRAPS
	DC.L	IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn
	DC.L	IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn
;org $000000C0
;FP/MMU
	DC.L	IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn
	DC.L	IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn,IntReturn

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;					Header
HEADER:
	DC.B	"SEGA GENESIS    "	        ;System Name	MUST TAKE UP 16 BYTES, USE PADDING IF NECESSARY
	DC.B	"(C)PDS  "			;Copyright	MUST TAKE UP 8 BYTES, USE PADDING IF NECESSARY
 	DC.B	"2022.JUN"			;Date		MUST TAKE UP 8 BYTES, USE PADDING IF NECESSARY
CARTNAME:
	DC.B 	"15 PUZZLE"
CARTNAME_END:
	DS.B 48-(CARTNAME_END-CARTNAME)	;ENSURES PROPER SPACING
CARTNAMEALT:
	DC.B	"15 PUZZLE"
CARTNAMEALT_END:
	DS.B 48-(CARTNAMEALT_END-CARTNAMEALT)	;ENSURES PROPER SPACING
gameID:
	DC.B	"GM PUPPY001-00"	        ;TT NNNNNNNN-RR T=Type (GM=Game) N=game Num  R=Revision
	DC.W	$0000				;16-bit Checksum (Address $000200+)
CTRLDATA:
	DC.B	"J               "	        ;Control Data (J=3button K=Keyboard 6=6button C=cdrom)
                                                ;(MUST TAKE UP 16 BYTES, USE PADDING IF NECESSARY)
ROMSTART:
	DC.L	$00000000			;ROM Start
ROMLEN:
	DC.L	$003FFFFF			;ROM Length
RAMSTART:
	DC.L	$00FF0000
RAMEND:
	DC.L	$00FFFFFF	;RAM start/end (fixed)

	DC.B	"            "		;External RAM Data	(MUST TAKE UP 12 BYTES, USE PADDING IF NECESSARY)
	DC.B	"            "		;Modem Data		(MUST TAKE UP 12 BYTES, USE PADDING IF NECESSARY)
MEMO:
	DC.B	"                                        "      ;(MUST TAKE UP 40 BYTES, USE PADDING IF NECESSARY)
REGION:
	DC.B	"JUE             "	;Regions Allowed        (MUST TAKE UP 16 BYTES, USE PADDING IF NECESSARY)
	even
HEADER_END:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;					Generic Interrupt Handler
IntReturn:
	rte                            ;immediately return to game
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;					Program Start
ProgramStart:
	;initialize TMSS (TradeMark Security System)
	move.b ($A10001),D0		;A10001 test the hardware version
	and.b #$0F,D0
	beq	NoTmss				;branch if no TMSS chip
	move.l #'SEGA',($A14000);A14000 disable TMSS
NoTmss:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;					Set Up Graphics

	lea VDPSettings,A5		      ;Initialize Screen Registers
	move.l #VDPSettingsEnd-VDPSettings,D1 ;length of Settings

	move.w (VDP_ctrl),D0	              ;C00004 read VDP status (interrupt acknowledge?)
	move.l #$00008000,d5	              ;VDP Reg command (%8rvv)

NextInitByte:
	move.b (A5)+,D5			      ;get next video control byte
	move.w D5,(VDP_ctrl)	              ;C00004 send write register command to VDP
		;   8RVV - R=Reg V=Value
	add.w #$0100,D5			      ;point to next VDP register
	dbra D1,NextInitByte	              ;loop for rest of block

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;       Set up palette

	;Define palette
	move.l #$C0000000,d0	;Color 0 (background)
	move.l d0,VDP_Ctrl
	;        ----BBB-GGG-RRR-
	move.w #%0000011000000000,VDP_data

	move.l #$C01E0000,d0	;Color 15 (Font)
	move.l d0,VDP_Ctrl
	move.w #%0000000011101110,VDP_data

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;					Set up Font
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; FONT IS 1BPP, THIS ROUTINE CONVERTS IT TO A 4BPP FORMAT.
	lea Font,A1					 ;Font Address in ROM
	move.l #Font_End-Font,d6	                 ;Our font contains 96 letters 8 lines each

	move.l #$40000000,(VDP_Ctrl);Start writes to VRAM address $0000
NextFont:
	move.b (A1)+,d0		;Get byte from font
	moveq.l #7,d5		;Bit Count (8 bits)
	clr.l d1		;Reset BuildUp Byte

Font_NextBit:			;1 color per nibble = 4 bytes

	rol.l #3,d1		;Shift BuildUp 3 bits left
	roxl.b #1,d0		;Shift a Bit from the 1bpp font into the Pattern
	roxl.l #1,d1		;Shift bit into BuildUp
	dbra D5,Font_NextBit    ;Next Bit from Font

	move.l d1,d0		; Make fontfrom Color 1 to color 15
	rol.l #1,d1		;Bit 1
	or.l d0,d1
	rol.l #1,d1		;Bit 2
	or.l d0,d1
	rol.l #1,d1		;Bit 3
	or.l d0,d1

	move.l d1,(VDP_Data);Write next Long of char (one line) to VDP
	dbra d6,NextFont	;Loop until done

	clr.b Cursor_X		;Clear Cursor XY
	clr.b Cursor_Y

	;Turn on screen
	move.w	#$8144,(VDP_Ctrl);C00004 reg 1 = 0x44 unblank display

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; all of the above was just the prep work to boot the Sega Genesis, and had nothing to do with a 15 Puzzle.
; That's hardware for you!

	LEA GameRam,A0
        ;load the initial state of the puzzle. There is no randomization here unfortunately, as creating a sufficient pseudo-RNG
        ;to make the game "believable" is more difficult than programming the game itself!
        ;so instead we'll start in such a manner that the player has to do quite a bit of work to win.
	MOVE.B #'F',(A0)+
	MOVE.B #'E',(A0)+
	MOVE.B #'D',(A0)+
	MOVE.B #'C',(A0)+
	MOVE.B #'B',(A0)+
	MOVE.B #'A',(A0)+
	MOVE.B #'9',(A0)+
	MOVE.B #'8',(A0)+
	MOVE.B #'7',(A0)+
	MOVE.B #'6',(A0)+
	MOVE.B #'5',(A0)+
	MOVE.B #'4',(A0)+
	MOVE.B #'3',(A0)+
	MOVE.B #'2',(A0)+
	MOVE.B #'1',(A0)+
	MOVE.B #' ',(A0)+

       ;puzzle will look like:
       ;FEDC
       ;BA98
       ;7654
       ;321

main:
	JSR Player_ReadControlsDual     ;get controller input
	move.w d0,(joypad1)

	;adjust the number of these as you see fit.
	;this affects the game's overall speed.
	JSR waitVBlank
	JSR waitVBlank
	JSR waitVBlank
	JSR waitVBlank
	JSR waitVBlank
	JSR waitVBlank
	JSR waitVBlank
	JSR waitVBlank
	JSR waitVBlank
	JSR waitVBlank

        ;find where the blank space is among GameRAM
	LEA GameRAM,a0
	MOVE.B #' ',D0
	JSR REPNE_SCASB
	MOVE.L A0,A1

;;;;;;;;;;;;;;;;;;; check controller presses
JOYPAD_BITFLAG_M equ 2048
JOYPAD_BITFLAG_Z equ 1024
JOYPAD_BITFLAG_Y equ 512
JOYPAD_BITFLAG_X equ 256
JOYPAD_BITFLAG_S equ 128
JOYPAD_BITFLAG_C equ 64
JOYPAD_BITFLAG_B equ 32
JOYPAD_BITFLAG_A equ 16
JOYPAD_BITFLAG_R equ 8
JOYPAD_BITFLAG_L equ 4
JOYPAD_BITFLAG_D equ 2
JOYPAD_BITFLAG_U equ 1

JOYPAD_BITNUM_M equ 11
JOYPAD_BITNUM_Z equ 10
JOYPAD_BITNUM_Y equ 9
JOYPAD_BITNUM_X equ 8
JOYPAD_BITNUM_S equ 7
JOYPAD_BITNUM_C equ 6
JOYPAD_BITNUM_B equ 5
JOYPAD_BITNUM_A equ 4
JOYPAD_BITNUM_R equ 3
JOYPAD_BITNUM_L equ 2
JOYPAD_BITNUM_D equ 1
JOYPAD_BITNUM_U equ 0

	move.w (joypad1),D0

	BTST #JOYPAD_BITNUM_U,D0
	BNE JoyNotUp
		MOVEM.L D0/A1,-(SP)
			ADDA.L #4,A1
			CMPA.L #GameRam_End,A1
			BHI .doNothing
			;OTHERWISE SWAP THE EMPTY SPACE WITH THE BYTE BELOW IT.
				MOVE.B (A1),D7
				MOVE.B (A0),(A1)
				MOVE.B D7,(A0)
.doNothing
		MOVEM.L (SP)+,D0/A1
		bra vdraw
JoyNotUp:
	BTST #JOYPAD_BITNUM_D,D0
	BNE JoyNotDown
		MOVEM.L D0/A1,-(SP)

			SUBA.L #4,A1		;CHECK ONE ROW ABOVE WHERE WE ARE
			CMPA.L #GameRam,A1
			BCS .doNothing		;if A1-4 IS BELOW THE START OF GAME RAM, DON'T MOVE
			;OTHERWISE SWAP THE EMPTY SPACE WITH THE BYTE ABOVE IT.
				MOVE.B (A1),D7
				MOVE.B (A0),(A1)
				MOVE.B D7,(A0)
.doNothing:
		MOVEM.L (SP)+,D0/A1
		bra vdraw
JoyNotDown:
	BTST #JOYPAD_BITNUM_L,D0
	BNE JoyNotLeft
		MOVEM.L D0/A1,-(SP)
			ADDA.L #1,A1
			MOVE.L A1,D4
			MOVE.L A0,D3
			AND.L #3,D4
			AND.L #3,D3
			CMP.L D3,D4
			BCS .doNothing
			;OTHERWISE SWAP THE EMPTY SPACE WITH THE BYTE TO THE LEFT
				MOVE.B (A1),D7
				MOVE.B (A0),(A1)
				MOVE.B D7,(A0)
.doNothing:
		MOVEM.L (SP)+,D0/A1
		bra vdraw
JoyNotLeft:
	BTST #JOYPAD_BITNUM_R,D0
	BNE JoyNotRight
		MOVEM.L D0/A1,-(SP)
			SUBA.L #1,A1
			MOVE.L A1,D4
			MOVE.L A0,D3
			AND.L #3,D4
			AND.L #3,D3
			CMP.L D3,D4
			BHI .doNothing
			;OTHERWISE SWAP THE EMPTY SPACE WITH THE BYTE TO THE RIGHT
				MOVE.B (A1),D7
				MOVE.B (A0),(A1)
				MOVE.B D7,(A0)
.doNothing:
		MOVEM.L (SP)+,D0/A1
		bra vdraw
JoyNotRight:

vdraw:
	;this actually draws the current state of the puzzle to the screen.
	LEA GameRam,A0
	CLR.B (Cursor_X)   ;reset text cursors to top left of screen
	CLR.B (Cursor_Y)

;draw the puzzle

;anything insize a REPT N...ENDR block is in-lined N times, back to back.
       rept 4

	      MOVE.B (A0)+,D0
	      JSR PrintChar

	      MOVE.B (A0)+,D0
	      JSR PrintChar

	      MOVE.B (A0)+,D0
	      JSR PrintChar

	      MOVE.B (A0)+,D0
	      JSR PrintChar         ;we just finished drawing one row of the puzzle. Now, begin a new line and continue drawing.

              jsr newline
       endr

checkIfWin:
	;YES THIS IS MESSY, I TRIED IT WITH A LOOP BUT IT WOULDN'T WORK SO I JUST UNROLLED THE LOOP.
	LEA GameRam,a4
	MOVE.B (A4)+,D5
	CMP.B #'1',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'2',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'3',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'4',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'5',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'6',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'7',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'8',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'9',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'A',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'B',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'C',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'D',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'E',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #'F',D5
	BNE .keepGoing

	MOVE.B (A4)+,D5
	CMP.B #' ',D5
	BNE .keepGoing

	clr.b (Cursor_X)
	move.b #7,(Cursor_Y)
	LEA victoryMessage,a3
	jsr PrintString
	jmp *  ;game freezes after you win.

.keepGoing:
;it's unlikely that the label "main" is in range of here so I didn't bother checking and just assumed it was out of range.
;Otherwise I would have said "BEQ main" instead of BNE .keepGoing
	jmp main

VictoryMessage:
	DC.B "A WINNER IS YOU",255
	EVEN

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
REPNE_SCASB:
	;INPUT:
	;A0 = POINTER TO START OF MEMORY
	;D0 = THE BYTE TO SEARCH FOR
	;OUTPUT = A0 POINTS TO THE BYTE THAT CONTAINED D0
	MOVE.B (A0),D1
	CMP.B D0,D1
	BEQ .done
	ADDA.L #1,A0
	BRA REPNE_SCASB
.done:
	RTS

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Player_ReadControlsDual:

	move.b #%01000000,($A1000B)	; Set direction IOIIIIII (I=In O=Out)
	move.l #$A10003,a0		;RW port for player 1

	move.b  #$40,(a0)	; TH = 1
	nop		;Delay
	nop
	move.b  (a0),d2		; d0.b = --CBRLDU	Store in D2

	move.b	#$0,(a0)	; TH = 0
	nop		;Delay
	nop
	move.b	(a0),d1		; d1.b = --SA--DU	Store in D1

	move.b  #$40,(a0)	; TH = 1
	nop		;Delay
	nop
	move.b	#$0,(a0)	; TH = 0
	nop		;Delay
	nop
	move.b  #$40,(a0)	; TH = 1
	nop		;Delay
	nop
	move.b	(a0),d3		; d1.b = --CBXYZM	Store in D3
	move.b	#$0,(a0)	; TH = 0

	clr.l d0			;Clear buildup byte
	roxr.b d2
	roxr.b d0			;U
	roxr.b d2
	roxr.b d0			;D
	roxr.b d2
	roxr.b d0			;L
	roxr.b d2
	roxr.b d0			;R
	roxr.b #5,d1
	roxr.b d0			;A
	roxr.b d2
	roxr.b d0			;B
	roxr.b d2
	roxr.b d0			;C
	roxr.b d1
	roxr.b d0			;S

	move.l d3,d1
	roxl.l #7,d1		;XYZ
	and.l #%0000011100000000,d1
	or.l d1,d0

	move.l d3,d1
	roxl.l #8,d1		;M
	roxl.l #3,d1
	and.l #%0000100000000000,d1
	or.l d1,d0

	or.l #$FFFFF000,d0	;Set unused bits to 1

	;this returns player 1's buttons into D0 as the following:
	;----MZYXSCBARLDU
	rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
waitVBlank:							;Bit 3 defines if we're in Vblank
	MOVE.L d0,-(sp)
.wait:
		move.w VDP_ctrl,d0
		and.w #%0000000000001000,d0		;See if vblank is running
		bne .wait					;wait until it is

waitVBlank2:
		move.w VDP_ctrl,d0
		and.w #%0000000000001000,d0		;See if vblank is running
		beq waitVBlank2					;wait until it isnt
	MOVE.L (SP)+,d0
	rts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

PrintChar:				;Show D0 to screen
	moveM.l d0-d7/a0-a7,-(sp)
		and.l #$FF,d0			;Keep only 1 byte
		sub #32,d0				;No Characters in our font below 32
PrintCharAlt:
		Move.L  #$40000003,d5	;top 4=write, bottom $3=Cxxx range
		clr.l d4					;Tilemap at $C000+

		Move.B (Cursor_Y),D4
		rol.L #8,D4				;move $-FFF to $-FFF----
		rol.L #8,D4
		rol.L #7,D4				;2 bytes per tile * 64 tiles per line
		add.L D4,D5				;add $4------3

		Move.B (Cursor_X),D4
		rol.L #8,D4				;move $-FFF to $-FFF----
		rol.L #8,D4
		rol.L #1,D4				;2 bytes per tile
		add.L D4,D5				;add $4------3

		MOVE.L	D5,(VDP_ctrl)	; C00004 write next character to VDP
		MOVE.W	D0,(VDP_data)	; C00000 store next word of name data

		addq.b #1,(Cursor_X)	;INC Xpos
		move.b (Cursor_X),d0
		cmp.b #39,d0
		bls nextpixel_Xok
		jsr NewLine			;If we're at end of line, start newline
nextpixel_Xok:
	moveM.l (sp)+,d0-d7/a0-a7
	rts

PrintString:
		move.b (a3)+,d0			;Read a character in from A3
		cmp.b #255,d0
		beq PrintString_Done	;return on 255
		jsr PrintChar			;Print the Character
		bra PrintString
PrintString_Done:
	rts

NewLine:
	addq.b #1,(Cursor_Y)		;INC Y
	clr.b (Cursor_X)			;Zero X
	rts

Font:
;1bpp font - 8x8 96 characters
;looks just like your typical "8-bit" font. You'll just have to take my word for it.
     DC.B $00,$00,$00,$00,$00,$00,$00,$00,$18,$3c,$3c,$18,$18,$00,$18,$18
     DC.B $36,$36,$12,$24,$00,$00,$00,$00,$00,$12,$7f,$24,$24,$fe,$48,$00
     DC.B $00,$04,$1e,$28,$1c,$0a,$3c,$10,$00,$62,$64,$08,$10,$26,$46,$00
     DC.B $00,$18,$24,$20,$12,$2c,$44,$3a,$18,$18,$08,$10,$00,$00,$00,$00
     DC.B $08,$10,$20,$20,$20,$20,$10,$08,$10,$08,$04,$04,$04,$04,$08,$10
     DC.B $00,$10,$38,$10,$28,$00,$00,$00,$00,$00,$10,$10,$7c,$10,$10,$00
     DC.B $00,$00,$00,$00,$0c,$0c,$04,$08,$00,$00,$00,$00,$7e,$00,$00,$00
     DC.B $00,$00,$00,$00,$00,$18,$18,$00,$01,$02,$04,$08,$10,$20,$40,$00
     DC.B $1c,$26,$63,$63,$63,$32,$1c,$00,$0c,$1c,$0c,$0c,$0c,$0c,$3f,$00
     DC.B $3e,$63,$07,$1e,$3c,$70,$7f,$00,$3f,$06,$0c,$1e,$03,$63,$3e,$00
     DC.B $0e,$1e,$36,$66,$7f,$06,$06,$00,$7e,$60,$7e,$03,$03,$63,$3e,$00
     DC.B $1e,$30,$60,$7e,$63,$63,$3e,$00,$7f,$63,$06,$0c,$18,$18,$18,$00
     DC.B $3c,$62,$72,$3c,$4f,$43,$3e,$00,$3e,$63,$63,$3f,$03,$06,$3c,$00
     DC.B $00,$18,$18,$00,$18,$18,$00,$00,$00,$0c,$0c,$00,$0c,$0c,$04,$08
     DC.B $00,$00,$06,$18,$60,$18,$06,$00,$00,$00,$00,$7e,$00,$7e,$00,$00
     DC.B $00,$00,$60,$18,$06,$18,$60,$00,$1c,$36,$36,$06,$0c,$00,$0c,$0c
     DC.B $3c,$42,$99,$a1,$a1,$99,$42,$3c,$1c,$36,$63,$63,$7f,$63,$63,$00
     DC.B $7e,$63,$63,$7e,$63,$63,$7e,$00,$1e,$33,$60,$60,$60,$33,$1e,$00
     DC.B $7c,$66,$63,$63,$63,$66,$7c,$00,$3f,$30,$30,$3e,$30,$30,$3f,$00
     DC.B $7f,$60,$60,$7e,$60,$60,$60,$00,$1f,$30,$60,$67,$63,$33,$1f,$00
     DC.B $63,$63,$63,$7f,$63,$63,$63,$00,$3f,$0c,$0c,$0c,$0c,$0c,$3f,$00
     DC.B $03,$03,$03,$03,$03,$63,$3e,$00,$63,$66,$6c,$78,$7c,$6e,$67,$00
     DC.B $30,$30,$30,$30,$30,$30,$3f,$00,$63,$77,$7f,$7f,$6b,$63,$63,$00
     DC.B $63,$73,$7b,$7f,$6f,$67,$63,$00,$3e,$63,$63,$63,$63,$63,$3e,$00
     DC.B $7e,$63,$63,$63,$7e,$60,$60,$00,$3e,$63,$63,$63,$6f,$66,$3d,$00
     DC.B $7e,$63,$63,$67,$7c,$6e,$67,$00,$3c,$66,$60,$3e,$03,$63,$3e,$00
     DC.B $3f,$0c,$0c,$0c,$0c,$0c,$0c,$00,$63,$63,$63,$63,$63,$63,$3e,$00
     DC.B $63,$63,$63,$77,$3e,$1c,$08,$00,$63,$63,$6b,$7f,$7f,$77,$63,$00
     DC.B $63,$77,$3e,$1c,$3e,$77,$63,$00,$33,$33,$33,$1e,$0c,$0c,$0c,$00
     DC.B $7f,$07,$0e,$1c,$38,$70,$7f,$00,$00,$38,$20,$20,$20,$20,$38,$00
     DC.B $80,$40,$20,$10,$08,$04,$02,$00,$00,$1c,$04,$04,$04,$04,$1c,$00
     DC.B $10,$28,$44,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$7e,$00
     DC.B $00,$20,$10,$00,$00,$00,$00,$00,$00,$18,$04,$1c,$24,$2c,$1c,$00
     DC.B $00,$20,$20,$38,$24,$24,$38,$00,$00,$00,$1c,$20,$20,$20,$1c,$00
     DC.B $00,$04,$04,$1c,$24,$24,$1c,$00,$00,$00,$1c,$24,$3c,$20,$1c,$00
     DC.B $00,$18,$24,$20,$30,$20,$20,$00,$00,$1c,$24,$24,$1c,$04,$3c,$00
     DC.B $00,$20,$20,$38,$24,$24,$24,$00,$00,$10,$00,$10,$10,$10,$10,$00
     DC.B $08,$00,$08,$08,$08,$08,$28,$10,$20,$20,$24,$28,$30,$28,$24,$00
     DC.B $10,$10,$10,$10,$10,$10,$18,$00,$00,$00,$40,$68,$54,$54,$54,$00
     DC.B $00,$00,$28,$34,$24,$24,$24,$00,$00,$00,$1c,$22,$22,$22,$1c,$00
     DC.B $00,$00,$38,$24,$24,$38,$20,$20,$00,$00,$1c,$24,$24,$1c,$04,$04
     DC.B $00,$00,$2c,$30,$20,$20,$20,$00,$00,$00,$1c,$20,$1c,$02,$3c,$00
     DC.B $00,$10,$3c,$10,$10,$14,$08,$00,$00,$00,$24,$24,$24,$24,$1a,$00
     DC.B $00,$00,$24,$24,$24,$14,$18,$00,$00,$00,$92,$92,$92,$5a,$6c,$00
     DC.B $00,$00,$22,$14,$08,$14,$22,$00,$00,$00,$24,$24,$1c,$04,$18,$00
     DC.B $00,$00,$3c,$04,$18,$20,$3c,$00,$00,$08,$10,$10,$20,$10,$10,$08
     DC.B $18,$18,$18,$18,$18,$18,$18,$18,$00,$10,$08,$08,$04,$08,$08,$10
     DC.B $00,$00,$00,$30,$4a,$04,$00,$00,$1c,$7f,$00,$7f,$55,$55,$55,$00
Font_End:

VDPSettings:
	DC.B $04 ; 0 mode register 1											---H-1M-
	DC.B $04 ; 1 mode register 2											-DVdP---
	DC.B $30 ; 2 name table base for scroll A (A=top 3 bits)				--AAA--- = $C000
	DC.B $3C ; 3 name table base for window (A=top 4 bits / 5 in H40 Mode)	--AAAAA- = $F000
	DC.B $07 ; 4 name table base for scroll B (A=top 3 bits)				-----AAA = $E000
	DC.B $6C ; 5 sprite attribute table base (A=top 7 bits / 6 in H40)		-AAAAAAA = $D800
	DC.B $00 ; 6 unused register											--------
	DC.B $00 ; 7 background color (P=Palette C=Color)						--PPCCCC
	DC.B $00 ; 8 unused register											--------
	DC.B $00 ; 9 unused register											--------
	DC.B $FF ;10 H interrupt register (L=Number of lines)					LLLLLLLL
	DC.B $00 ;11 mode register 3											----IVHL
	DC.B $81 ;12 mode register 4 (C bits both1 = H40 Cell)					C---SIIC
	DC.B $37 ;13 H scroll table base (A=Top 6 bits)							--AAAAAA = $FC00
	DC.B $00 ;14 unused register											--------
	DC.B $02 ;15 auto increment (After each Read/Write)						NNNNNNNN
	DC.B $01 ;16 scroll size (Horiz & Vert size of ScrollA & B)				--VV--HH = 64x32 tiles
	DC.B $00 ;17 window H position (D=Direction C=Cells)					D--CCCCC
	DC.B $00 ;18 window V position (D=Direction C=Cells)					D--CCCCC
	DC.B $FF ;19 DMA length count low										LLLLLLLL
	DC.B $FF ;20 DMA length count high										HHHHHHHH
	DC.B $00 ;21 DMA source address low										LLLLLLLL
	DC.B $00 ;22 DMA source address mid										MMMMMMMM
	DC.B $80 ;23 DMA source address high (C=CMD)							CCHHHHHH
VDPSettingsEnd:
	even


/* ARM assembly AARCH64 Raspberry PI 3B */
/*  program puzzle15_64.s   */

/*******************************************/
/* Constantes file                         */
/*******************************************/
/* for this file see task include a file in language AArch64 assembly*/
.include "../includeConstantesARM64.inc"

.equ NBBOX,  16
.equ GRAINE,  123456      // change for other game
.equ NBSHUFFLE, 4
.equ KEYSIZE,  8

.equ IOCTL,     0x1D  // Linux syscall
.equ SIGACTION, 0x86   // Linux syscall
.equ SYSPOLL,   0x16  // Linux syscall
.equ CREATPOLL, 0x14  // Linux syscall
.equ CTLPOLL,   0x15  // Linux syscall

.equ TCGETS,    0x5401
.equ TCSETS,    0x5402
.equ ICANON,    2
.equ ECHO,     10
.equ POLLIN,    1
.equ EPOLL_CTL_ADD,    1

.equ SIGINT,   2    // Issued if the user sends an interrupt signal (Ctrl + C)
.equ SIGQUIT,  3    // Issued if the user sends a quit signal (Ctrl + D)
.equ SIGTERM, 15    // Software termination signal (sent by kill by default)
.equ SIGTTOU, 22    //

/*******************************************/
/* Structures                               */
/********************************************/
/* structure termios see doc linux*/
    .struct  0
term_c_iflag:                    // input modes
    .struct  term_c_iflag + 4
term_c_oflag:                    // output modes
    .struct  term_c_oflag + 4
term_c_cflag:                    // control modes
    .struct  term_c_cflag + 4
term_c_lflag:                    // local modes
    .struct  term_c_lflag + 4
term_c_cc:                       // special characters
    .struct  term_c_cc + 20      // see length if necessary
term_fin:

/* structure sigaction see doc linux */
    .struct  0
sa_handler:
    .struct  sa_handler + 8
sa_mask:
    .struct  sa_mask + 8
sa_flags:
    .struct  sa_flags + 8
sa_sigaction:
    .struct  sa_sigaction + 8
sa_fin:

/* structure poll see doc linux */
    .struct  0
poll_event:
    .struct  poll_event + 8
poll_fd:                            //   File Descriptor
    .struct  poll_fd + 8
poll_fin:
/*********************************/
/* Initialized data              */
/*********************************/
.data
sMessResult:           .ascii " "
sMessValeur:           .fill 11, 1, ' '             // size => 11
szCarriageReturn:      .asciz "\n"
szMessGameWin:         .asciz "You win in @ move number !!!!\n"
szMessMoveError:       .asciz "Huh... Impossible move !!!!\n"
szMessErreur:          .asciz "Error detected.\n"
szMessErrInitTerm:     .asciz "Error terminal init.\n"
szMessErrInitPoll:     .asciz "Error poll init.\n"
szMessErreurKey:       .asciz "Error read key.\n"
szMessSpaces:          .asciz "    "
qGraine:               .quad GRAINE
szMessErr:             .asciz    "Error code hexa : @ décimal : @ \n"

szClear:     .byte 0x1B
             .byte 'c'                         // console clear
             .byte 0
/*********************************/
/* UnInitialized data            */
/*********************************/
.bss
.align 4
sZoneConv:      .skip 24
qCodeError:     .skip 8
ibox:           .skip 4 * NBBOX                // game boxes
qEnd:           .skip 8                        // 0 loop  1 = end loop
qTouche:        .skip KEYSIZE                  // value key pressed
stOldtio:       .skip term_fin                 // old terminal state
stCurtio:       .skip term_fin                 // current terminal state
stSigAction:    .skip sa_fin                   // area signal structure
stSigAction1:   .skip sa_fin
stSigAction2:   .skip sa_fin
stSigAction3:   .skip sa_fin
stPoll1:        .skip poll_fin                 // area poll structure
stPoll2:        .skip poll_fin
stevents:       .skip 16
/*********************************/
/*  code section                 */
/*********************************/
.text
.global main
main:                                // entry of program
    mov x0,#0
    bl initTerm                      // terminal init
    cmp x0,0                         // error ?
    blt 100f
    bl initPoll                      // epoll instance init
    cmp x0,0
    blt 99f
    mov x22,x0                       // save epfd
    ldr x2,qAdribox
    mov x9,#0                        // init counter
    mov x0,0
1:                                   // loop init boxs
    add x1,x0,#1                     // box value
    str w1,[x2,x0, lsl #2]           // store value
    add x0,x0,#1                     // increment counter
    cmp x0,#NBBOX - 2                // end ?
    ble 1b
    mov x10,#15                      // empty box location
    ldr x0,qAdribox
    bl shuffleGame
2:                                   // loop moves
    ldr x0,qAdribox
    bl displayGame
3:
    mov x0,x22                       // epfd
    bl waitKey
    cmp x0,0
    beq 3b                           // no ket pressed -> loop
    blt 99f                          // error ?
    bl readKey                       // read key
    cmp x0,#-1
    beq 99f                          // error
    cmp x0,3                         // <ctrl_C>
    beq 5f
    cmp x0,113                       // saisie q (quit) ?
    beq 5f
    cmp x0,81                        // saisie Q  (Quit)?
    beq 5f
    mov x1,x0                        // key
    ldr x0,qAdribox
    bl keyMove                       // analyze key move
    ldr x0,qAdribox
    bl gameOK                        // end game ?
    cmp x0,#1
    bne 2b                           // no -> loop
                                     // win
    mov x0,x9                        // move counter
    ldr x1,qAdrsZoneConv
    bl conversion10
    ldr x0,qAdrszMessGameWin
    ldr x1,qAdrsZoneConv
    bl strInsertAtCharInc            // insert result at @ character
    bl affichageMess
5:
    bl restauTerm                    // terminal restaur
    mov x0, #0                       // return code
    b 100f
99:
    bl restauTerm                    // terminal restaur
    mov x0,1                         // return code error
    b 100f
100:                                 // standard end of the program
    mov x8, #EXIT                    // request to exit program
    svc #0                           // perform the system call

qAdrsMessValeur:          .quad sMessValeur
qAdrszCarriageReturn:     .quad szCarriageReturn
qAdrsMessResult:          .quad sMessResult
qAdribox:                 .quad ibox
qAdrszMessGameWin:        .quad szMessGameWin
qAdrstevents:             .quad stevents
qAdrszMessErreur:         .quad szMessErreur
qAdrstOldtio:             .quad stOldtio
qAdrstCurtio:             .quad stCurtio
qAdrstSigAction:          .quad stSigAction
qAdrstSigAction1:         .quad stSigAction1
qAdrSIG_IGN:              .quad 1
qAdrqEnd:                 .quad qEnd
qAdrqTouche:              .quad qTouche
qAdrszMessErrInitTerm:    .quad szMessErrInitTerm
qAdrszMessErrInitPoll:    .quad szMessErrInitPoll
qAdrszMessErreurKey:      .quad szMessErreurKey
/******************************************************************/
/*     key move                                                   */
/******************************************************************/
/* x0 contains boxs address           */
/* x1 contains key value               */
/* x9 move counter                     */
/* x10 contains location empty box    */
keyMove:
    stp x1,lr,[sp,-16]!              // save  registers
    mov x7,x0
    lsr x1,x1,16
    cmp x1,#0x42                     // down arrow
    bne 1f
    cmp x10,#4                       // if x10 < 4   error
    blt 80f
    sub x2,x10,#4                    // compute location
    b 90f
1:
    cmp x1,#0x41                     // high arrow
    bne 2f
    cmp x10,#11                      // if x10 > 11   error
    bgt 80f
    add x2,x10,#4                    // compute location
    b 90f
2:
    cmp x1,#0x43                     // right arrow
    bne 3f
    tst x10,#0b11                    // if x10 = 0,4,8,12   error
    beq 80f
    sub x2,x10,#1                    // compute location
    b 90f
3:
    cmp x1,#0x44                     // left arrow
    bne 100f
    and x3,x10,#0b11                 // error if x10 = 3 7 11 and 15
    cmp x3,#3
    beq 80f
    add x2,x10,#1                    // compute location
    b 90f

80:                                  // move error
    ldr x0,qAdrqCodeError
    mov x1,#1
    str x1,[x0]
    b 100f
90:                                  // white box and move box inversion
    ldr w3,[x7,x2,lsl #2]
    str w3,[x7,x10,lsl #2]
    mov x10,x2
    mov x3,#0
    str w3,[x7,x10,lsl #2]
    add x9,x9,#1                        // increment move counter
100:
    ldp x1,lr,[sp],16              // restaur  2 registers
    ret                            // return to address lr x30
qAdrqCodeError:             .quad qCodeError
/******************************************************************/
/*     shuffle game                                       */
/******************************************************************/
/* x0 contains boxs address           */
shuffleGame:
    stp x1,lr,[sp,-16]!            // save  registers
    stp x2,x3,[sp,-16]!            // save  registers
    stp x4,x5,[sp,-16]!            // save  registers
    mov x1,x0
    mov x0,NBSHUFFLE
    bl genereraleas
    lsl x4,x0,#1
1:
    mov x0,#14
    bl genereraleas
    add x3,x0,#1
    mov x0,#14
    bl genereraleas
    add x5,x0,#1
    ldr w2,[x1,x3,lsl #2]
    ldr w0,[x1,x5,lsl #2]
    str w2,[x1,x5,lsl #2]
    str w0,[x1,x3,lsl #2]
    subs x4,x4,#1
    bgt 1b

100:
    ldp x4,x5,[sp],16              // restaur  2 registers
    ldp x2,x3,[sp],16              // restaur  2 registers
    ldp x1,lr,[sp],16              // restaur  2 registers
    ret                            // return to address lr x30
/******************************************************************/
/*     game Ok ?                                      */
/******************************************************************/
/* x0 contains boxs address           */
gameOK:
    stp x1,lr,[sp,-16]!            // save  registers
    stp x2,x3,[sp,-16]!            // save  registers
    mov x2,#0
    ldr w3,[x0,x2,lsl #2]
    add x2,x2,#1
1:
    ldr w1,[x0,x2,lsl #2]
    cmp w1,w3
    bge 2f
    mov x0,#0                      // game not Ok
    b 100f
2:
    mov x3,x1
    add x2,x2,#1
    cmp x2,#NBBOX -2
    ble 1b
    mov x0,#1                      // game Ok

100:
    ldp x2,x3,[sp],16              // restaur  2 registers
    ldp x1,lr,[sp],16              // restaur  2 registers
    ret                            // return to address lr x30
/******************************************************************/
/*     display game                                       */
/******************************************************************/
/* x0 contains boxs address           */
displayGame:
    stp x1,lr,[sp,-16]!            // save  registers
                                   // clear screen !
    mov x4,x0
    ldr x0,qAdrszClear
    bl affichageMess
    mov x2,#0
    ldr x1,qAdrsMessValeur
1:
    ldr w0,[x4,x2,lsl #2]
    cmp w0,#0
    bne 2f
    ldr w0,iSpaces                    // store spaces
    str w0,[x1]
    b 3f
2:
    bl conversion10                     // call conversion decimal
    cmp x0,1
    beq 21f
    mov x0,0x20
    strh w0,[x1,#2]
    b 3f
21:
    mov w0,0x2020
    str w0,[x1,#1]
3:
    ldr x0,qAdrsMessResult
    bl affichageMess                    // display message
    add x0,x2,#1
    tst x0,#0b11
    bne 4f
    ldr x0,qAdrszCarriageReturn
    bl affichageMess                    // display message
4:
    add x2,x2,#1
    cmp x2,#NBBOX - 1
    ble 1b
    ldr x0,qAdrszCarriageReturn
    bl affichageMess                    // display line return
    ldr x0,qAdrqCodeError               // error detected ?
    ldr x1,[x0]
    cmp x1,#0
    beq 100f
    mov x1,#0                           // raz error code
    str x1,[x0]
    ldr x0,qAdrszMessMoveError          // display error message
    bl affichageMess
100:
    ldp x1,lr,[sp],16                   // restaur  2 registers
    ret                                 // return to address lr x30
iSpaces:              .int 0x00202020   // spaces
qAdrszClear:          .quad szClear
qAdrszMessMoveError:  .quad szMessMoveError

/***************************************************/
/*   Generation random number                  */
/***************************************************/
/* x0 contains limit  */
genereraleas:
    stp x1,lr,[sp,-16]!            // save  registers
    stp x2,x3,[sp,-16]!            // save  registers
    ldr x1,qAdrqGraine
    ldr x2,[x1]
    ldr x3,qNbDep1
    mul x2,x3,x2
    ldr x3,qNbDep2
    add x2,x2,x3
    str x2,[x1]                    // maj de la graine pour l appel suivant
    cmp x0,#0
    beq 100f
    udiv x3,x2,x0
    msub x0,x3,x0,x2               // résult = remainder

100:                               // end function
    ldp x2,x3,[sp],16              // restaur  2 registers
    ldp x1,lr,[sp],16              // restaur  2 registers
    ret                            // return to address lr x30
/*****************************************************/
qAdrqGraine: .quad qGraine
qNbDep1:     .quad 0x0019660d
qNbDep2:     .quad 0x3c6ef35f

/******************************************************************/
/*     traitement du signal                                       */
/******************************************************************/
sighandler:
    stp x0,lr,[sp,-16]!            // save  registers
    str x1,[sp,-16]!
    ldr x0,qAdrqEnd
    mov x1,#1                      // maj zone end
    str x1,[x0]
    ldr x1,[sp],16
    ldp x0,lr,[sp],16              // restaur  2 registers
    ret                            // return to address lr x30
/***************************************************/
/*   display error message                         */
/***************************************************/
/* x0 contains error code  x1 : message address */
displayError:
    stp x2,lr,[sp,-16]!            // save  registers
    mov x2,x0                      // save error code
    mov x0,x1
    bl affichageMess
    mov x0,x2                      // error code
    ldr x1,qAdrsZoneConv
    bl conversion16                // conversion hexa
    ldr x0,qAdrszMessErr           // display error message
    ldr x1,qAdrsZoneConv
    bl strInsertAtCharInc               // insert result at @ character
    mov x3,x0
    mov x0,x2                      // error code
    ldr x1,qAdrsZoneConv               // result address
    bl conversion10S                // conversion decimale
    mov x0,x3
    ldr x1,qAdrsZoneConv
    bl strInsertAtCharInc               // insert result at @ character
    bl affichageMess
100:
    ldp x2,lr,[sp],16              // restaur  2 registers
    ret                            // return to address lr x30
qAdrszMessErr:                 .quad szMessErr
qAdrsZoneConv:                 .quad sZoneConv
/*********************************/
/* init terminal state            */
/*********************************/
initTerm:
    stp x1,lr,[sp,-16]!            // save  registers
    /* read terminal state */
    mov x0,STDIN                   // input console
    mov x1,TCGETS
    ldr x2,qAdrstOldtio
    mov x8,IOCTL                   // call system Linux
    svc 0
    cbnz x0,98f                    // error ?

    adr x0,sighandler              // adresse routine traitement signal
    ldr x1,qAdrstSigAction         // adresse structure sigaction
    str x0,[x1,sa_handler]         // maj handler
    mov x0,SIGINT                  // signal type
    ldr x1,qAdrstSigAction
    mov x2,0
    mov x3,8
    mov x8,SIGACTION               // call system
    svc 0

    cmp x0,0                       // error ?
    bne 98f
    mov x0,SIGQUIT
    ldr x1,qAdrstSigAction
    mov x2,0                       // NULL
    mov x8,SIGACTION               // call system
    svc 0
    cmp x0,0                       // error ?
    bne 98f
    mov x0,SIGTERM
    ldr x1,qAdrstSigAction
    mov x2,0                       // NULL
    mov x8,SIGACTION               // appel systeme
    svc 0
    cmp x0,0
    bne 98f
    //
    adr x0,qAdrSIG_IGN             // address signal igonre function
    ldr x1,qAdrstSigAction1
    str x0,[x1,sa_handler]
    mov x0,SIGTTOU                 //invalidate other process signal
    ldr x1,qAdrstSigAction1
    mov x2,0                       // NULL
    mov x8,SIGACTION               // call system
    svc 0
    cmp x0,0
    bne 98f
    //
    /* read terminal current state  */
    mov x0,STDIN
    mov x1,TCGETS
    ldr x2,qAdrstCurtio            // address current termio
    mov x8,IOCTL                   // call systeme
    svc 0
    cmp x0,0                       // error ?
    bne 98f
    mov x2,ICANON | ECHO           // no key pressed echo on display
    mvn x2,x2                      // and one key
    ldr x1,qAdrstCurtio
    ldr x3,[x1,#term_c_lflag]
    and x3,x2,x2                   // add flags
    str x3,[x1,#term_c_lflag]      // and store
    mov x0,STDIN                   // maj terminal current state
    mov x1,TCSETS
    ldr x2,qAdrstCurtio
    mov x8,IOCTL                   // call system
    svc 0
    cbz x0,100f
98:                                // error display
    ldr x1,qAdrszMessErrInitTerm
    bl   displayError
    mov x0,-1
100:
    ldp x1,lr,[sp],16              // restaur  2 registers
    ret                            // return to address lr x30
qAdrstSigAction2:    .quad stSigAction2
qAdrstSigAction3:    .quad stSigAction3
/*********************************/
/* init instance epool            */
/*********************************/
initPoll:
    stp x1,lr,[sp,-16]!            // save  registers
    ldr x0,qAdrstevents
    mov x1,STDIN                   // maj structure events
    str x1,[x0,#poll_fd]           // maj FD
    mov x1,POLLIN                  // action code
    str x1,[x0,#poll_event]
    mov x0,0
    mov x8,CREATPOLL               // create epoll instance
    svc 0
    cmp x0,0                       // error ?
    ble 98f
    mov x10,x0                     // return FD epoll instance
    mov x1,EPOLL_CTL_ADD
    mov x2,STDIN                   // Fd to we want add
    ldr x3,qAdrstevents            // structure events address
    mov x8,CTLPOLL                 // call system control epoll
    svc 0
    cmp x0,0                       // error ?
    blt 98f                       // no
    mov x0,x10                     // return FD epoll instance
    b 100f
98:                                // error display
    ldr x1,qAdrszMessErrInitPoll   // error message
    bl   displayError
    mov x0,-1
100:
    ldp x1,lr,[sp],16              // restaur  2 registers
    ret                            // return to address lr x30
/*********************************/
/* wait key                      */
/*********************************/
/* x0 contains FD poll    */
waitKey:
    stp x1,lr,[sp,-16]!            // save  registers
    ldr x11,qAdrqTouche            // key address
    str xzr,[x11]                  // raz key
1:
    ldr x1,qAdrqEnd                // if signal ctrl-c  -> end
    ldr x1,[x1]
    cbnz x1,100f

    ldr x1,qAdrstevents
    mov x2,12                      // size events
    mov x3,1                       // timeout = 1  TODO: ??
    mov x4,0
    mov x8,SYSPOLL                 // call system wait POLL
    svc 0
    cmp x0,0                       // key pressed ?
    bge 100f
98:                                // error display
    ldr x1,qAdrszMessErreurKey        // error message
    bl   displayError
    mov x0,-1
100:
    ldp x1,lr,[sp],16              // restaur  2 registers
    ret                            // return to address lr x30
/*********************************/
/* read key                      */
/*********************************/
/* x0 returns key value */
readKey:
    stp x1,lr,[sp,-16]!            // save  registers
    mov x0,STDIN                   // File Descriptor
    ldr x1,qAdrqTouche             // buffer address
    mov x2,KEYSIZE                 // key size
    mov x8,READ                    // read key
    svc #0
    cmp x0,0                       // error ?
    ble 98f
    ldr x2,qAdrqTouche             // key address
    ldr x0,[x2]
    b 100f
98:                                // error display
    ldr x1,qAdrszMessErreur        // error message
    bl   displayError
    mov x0,-1
100:
    ldp x1,lr,[sp],16              // restaur  2 registers
    ret                            // return to address lr x30
/*********************************/
/* restaur terminal state        */
/*********************************/
restauTerm:
    stp x1,lr,[sp,-16]!            // save  registers
    mov x0,STDIN                   // end then restaur begin state terminal
    mov x1,TCSETS
    ldr x2,qAdrstOldtio
    mov x8,IOCTL                   // call system
    svc 0
    cbz x0,100f
    ldr x1,qAdrszMessErreur        // error message
    bl   displayError
100:
    ldp x1,lr,[sp],16              // restaur  2 registers
    ret                            // return to address lr x30

/********************************************************/
/*        File Include fonctions                        */
/********************************************************/
/* for this file see task include a file in language AArch64 assembly */
.include "../includeARM64.inc"


DEFINE BOARDSIZE="16"
DEFINE X0="13"
DEFINE Y0="6"
DEFINE ITEMW="3"
DEFINE ITEMH="2"

BYTE ARRAY board(BOARDSIZE)
BYTE emptyX,emptyY,solved,first=[1]

BYTE FUNC Index(BYTE x,y)
RETURN (x+y*4)

PROC UpdateItem(BYTE x,y)
  BYTE item

  Position(X0+x*ITEMW+1,Y0+y*ITEMH+1)
  item=board(Index(x,y))
  IF item=0 THEN
    Print("  ")
  ELSEIF item<10 THEN
    Put(160) Put(item+176)
  ELSE
    Put(item/10+176)
    Put(item MOD 10+176)
  FI
RETURN

PROC UpdateBoard()
  BYTE x,y

  FOR y=0 TO 3
  DO
    FOR x=0 TO 3
    DO
      UpdateItem(x,y)
    OD
  OD
RETURN

PROC DrawGrid()
  CHAR ARRAY
    top=[13 17 18 18 23 18 18 23 18 18 23 18 18 5],
    row=[13 124 32 32 124 32 32 124 32 32 124 32 32 124],
    mid=[13 1 18 18 19 18 18 19 18 18 19 18 18 4],
    bot=[13 26 18 18 24 18 18 24 18 18 24 18 18 3]
  BYTE y,i

  y=Y0
  Position(X0,y) Print(top) y==+1
  Position(X0,y) Print(row) y==+1
  FOR i=0 TO 2
  DO
    Position(X0,y) Print(mid) y==+1
    Position(X0,y) Print(row) y==+1
  OD
  Position(X0,y) Print(bot)
RETURN

PROC DrawBoard()
  DrawGrid()
  UpdateBoard()
RETURN

PROC FindEmpty()
  BYTE i

  FOR i=0 TO BOARDSIZE-1
  DO
    IF board(i)=0 THEN
      emptyX=i MOD 4
      emptyY=i/4
    FI
  OD
RETURN

PROC Wait(BYTE frames)
  BYTE RTCLOK=$14
  frames==+RTCLOK
  WHILE frames#RTCLOK DO OD
RETURN

PROC UpdateStatus()
  Position(9,3) Print("Game status: ")
  IF solved THEN
    Print("SOLVED !")
    IF first=0 THEN
      Sound(0,100,10,5) Wait(5)
      Sound(0,60,10,5) Wait(5)
      Sound(0,40,10,5) Wait(5)
      Sound(0,0,0,0)
    FI
    first=0
  ELSE
    Print("Shuffled")
  FI
RETURN

PROC InitBoard()
  BYTE i

  FOR i=1 TO BOARDSIZE
  DO
    board(i-1)=i MOD 16
  OD
  FindEmpty()
  solved=1
  UpdateStatus()
RETURN

BYTE FUNC IsSolved()
  BYTE i

  FOR i=1 TO BOARDSIZE
  DO
    IF board(i-1)#i MOD 16 THEN
      RETURN (0)
    FI
  OD
RETURN (1)

PROC CheckStatus()
  BYTE tmp

  tmp=IsSolved()
  IF solved#tmp THEN
    solved=tmp
    UpdateStatus()
  FI
RETURN

PROC Swap(BYTE x1,y1,x2,y2)
  BYTE tmp,i1,i2

  i1=Index(x1,y1)
  i2=Index(x2,y2)
  tmp=board(i1)
  board(i1)=board(i2)
  board(i2)=tmp
  UpdateItem(x1,y1)
  UpdateItem(x2,y2)
  CheckStatus()
RETURN

PROC Shuffle()
  BYTE i,j,tmp

  i=BOARDSIZE-1
  WHILE i>0
  DO
    j=Rand(i)
    tmp=board(i)
    board(i)=board(j)
    board(j)=tmp
    i==-1
  OD
  FindEmpty()
  UpdateBoard()
  CheckStatus()
RETURN

PROC MoveLeft()
  IF emptyX=0 THEN RETURN FI
  Swap(emptyX,emptyY,emptyX-1,emptyY)
  emptyX==-1
RETURN

PROC MoveRight()
  IF emptyX=3 THEN RETURN FI
  Swap(emptyX,emptyY,emptyX+1,emptyY)
  emptyX==+1
RETURN

PROC MoveUp()
  IF emptyY=0 THEN RETURN FI
  Swap(emptyX,emptyY,emptyX,emptyY-1)
  emptyY==-1
RETURN

PROC MoveDown()
  IF emptyY=3 THEN RETURN FI
  Swap(emptyX,emptyY,emptyX,emptyY+1)
  emptyY==+1
RETURN

PROC Main()
  BYTE k,lastStick=[255],currStick,
    CH=$02FC, ;Internal hardware value for last key pressed
    CRSINH=$02F0 ;Controls visibility of cursor

  Graphics(0)
  SetColor(2,0,2)
  CRSINH=1 ;hide cursor
  Position(10,18) Print("Joystick - move tiles")
  Position(9,19) Print("Space bar - shuffle")
  Position(15,20) Print("ESC - exit")
  InitBoard()
  DrawBoard()
  DO
    currStick=Stick(0)
    IF currStick#lastStick THEN
      IF currStick=11 THEN MoveRight()
      ELSEIF currStick=7 THEN MoveLeft()
      ELSEIF currStick=13 THEN MoveUp()
      ELSEIF currStick=14 THEN MoveDown()
      FI
    FI
    lastStick=currStick
    k=CH
    IF k#$FF THEN CH=$FF FI
    IF k=33 THEN Shuffle()
    ELSEIF k=28 THEN EXIT
    FI
  OD
RETURN


generic
   Rows, Cols: Positive;
   with function Name(N: Natural) return String; -- with Pre => (N < Rows*Cols);
   -- Name(0) shall return the name for the empty tile
package Generic_Puzzle is

   subtype Row_Type is Positive range 1 .. Rows;
   subtype Col_Type is Positive range 1 .. Cols;
   type Moves is (Up, Down, Left, Right);
   type Move_Arr is array(Moves) of Boolean;

   function Get_Point(Row: Row_Type; Col: Col_Type) return String;
   function Possible return Move_Arr;
   procedure Move(The_Move: Moves);

end Generic_Puzzle;


fpg←{⎕IO←0
    ⍺←4 4
    (s∨.<0)∨2≠⍴s←⍺:'invalid shape:'s
    0≠⍴⍴⍵:'invalid shuffle count:'⍵
    d←d,-d←↓2 2⍴3↑1
    e←¯1+⍴c←'↑↓←→○'
    b←w←s⍴w←1⌽⍳×/s
    z←⊃{
        z p←⍵
        n←(?⍴p)⊃p←(p≡¨(⊂s)|p)/p←(d~p)+⊂z
        b[z n]←b[n z]
        -⍨\n z
    }⍣⍵⊢(s-1)0
    ⎕←b
    ⍬{
        b≡w:'win'
        0=⍴⍺:⍞∇ ⍵
        e=i←c⍳m←⊃⍺:'quit'
        i>e:⍞∇ ⍵⊣⎕←'invalid direction:'m
        n≢s|n←⍵+i⊃d:⍞∇ ⍵⊣'out of bounds:'m
        b[⍵ n]←b[n ⍵]
        ⎕←(s×0≠⍴⍺)⍴b
        (1↓⍺)∇ n
    }z
}


/* ARM assembly Raspberry PI  */
/*  program puzzle15.s   */

/************************************/
/* Constantes                       */
/************************************/
.equ STDIN,  0     @ Linux input console
.equ STDOUT, 1     @ Linux output console
.equ EXIT,   1     @ Linux syscall
.equ READ,   3     @ Linux syscall
.equ WRITE,  4     @ Linux syscall

.equ IOCTL,     0x36  @ Linux syscall
.equ SIGACTION, 0x43  @ Linux syscall
.equ SYSPOLL,   0xA8  @ Linux syscall

.equ TCGETS,    0x5401
.equ TCSETS,    0x5402
.equ ICANON,    2
.equ ECHO,     10
.equ POLLIN,    1

.equ SIGINT,   2    @ Issued if the user sends an interrupt signal (Ctrl + C)
.equ SIGQUIT,  3    @ Issued if the user sends a quit signal (Ctrl + D)
.equ SIGTERM, 15    @ Software termination signal (sent by kill by default)
.equ SIGTTOU, 22    @

.equ NBBOX,  16
.equ TAILLEBUFFER,   10

/*******************************************/
/* Structures                               */
/********************************************/
/* structure termios see doc linux*/
    .struct  0
term_c_iflag:                    @ input modes
    .struct  term_c_iflag + 4
term_c_oflag:                    @ output modes
    .struct  term_c_oflag + 4
term_c_cflag:                    @ control modes
    .struct  term_c_cflag + 4
term_c_lflag:                    @ local modes
    .struct  term_c_lflag + 4
term_c_cc:                       @ special characters
    .struct  term_c_cc + 20      @ see length if necessary
term_fin:

/* structure sigaction see doc linux */
    .struct  0
sa_handler:
    .struct  sa_handler + 4
sa_mask:
    .struct  sa_mask + 4
sa_flags:
    .struct  sa_flags + 4
sa_sigaction:
    .struct  sa_sigaction + 4
sa_fin:

/* structure poll see doc linux */
    .struct  0
poll_fd:                            @   File Descriptor
    .struct  poll_fd + 4
poll_events:                        @  events mask
    .struct  poll_events + 4
poll_revents:                       @ events returned
    .struct  poll_revents + 4
poll_fin:
/*********************************/
/* Initialized data              */
/*********************************/
.data
sMessResult:           .ascii " "
sMessValeur:           .fill 11, 1, ' '             @ size => 11
szCarriageReturn:      .asciz "\n"
szMessGameWin:         .ascii "You win in "
sMessCounter:           .fill 11, 1, ' '            @ size => 11
                       .asciz " move number !!!!\n"
szMessMoveError:       .asciz "Huh... Impossible move !!!!\n"
szMessErreur:          .asciz "Error detected.\n"
szMessSpaces:          .asciz "    "
iGraine:               .int 123456
/*************************************************/
szMessErr: .ascii	"Error code hexa : "
sHexa: .space 9,' '
         .ascii "  decimal :  "
sDeci: .space 15,' '
         .asciz "\n"
szClear:     .byte 0x1B
		     .byte 'c'                         @ console clear
		     .byte 0
/*********************************/
/* UnInitialized data            */
/*********************************/
.bss
.align 4
iCodeError:     .skip 4
ibox:          .skip 4 * NBBOX                 @ game boxes
iEnd:           .skip 4                        @ 0 loop  1 = end loop
iTouche:        .skip 4                        @ value key pressed
stOldtio:       .skip term_fin                 @ old terminal state
stCurtio:       .skip term_fin                 @ current terminal state
stSigAction:    .skip sa_fin                   @ area signal structure
stSigAction1:   .skip sa_fin
stPoll1:        .skip poll_fin                 @ area poll structure
stPoll2:        .skip poll_fin
/*********************************/
/*  code section                 */
/*********************************/
.text
.global main
main:                                @ entry of program
    mov r0,#0
    ldr r2,iAdribox
    mov r9,#0                        @ move counter
1:                                   @ loop init boxs
    add r1,r0,#1                     @ box value
    str r1,[r2,r0, lsl #2]           @ store value
    add r0,#1                        @ increment counter
    cmp r0,#NBBOX - 2                @ end ?
    ble 1b
    mov r10,#15                      @ empty box location
    ldr r0,iAdribox
    bl shuffleGame
2:                                   @ loop moves
    ldr r0,iAdribox
    bl displayGame
    //ldr r0,iAdribox
    //bl gameOK                      @ end game ?
    //cmp r0,#1
    //beq 50f
    bl readKey                       @ read key
    cmp r0,#-1
    beq 100f                         @ error or control-c
    mov r1,r0                        @ key
    ldr r0,iAdribox
    bl keyMove
    ldr r0,iAdribox
    bl gameOK                        @ end game ?
    cmp r0,#1
    bne 2b                           @ no -> loop
50:                                  @ win
    mov r0,r9                        @ move counter
    ldr r1,iAdrsMessCounter
    bl conversion10
    ldr r0,iAdrszMessGameWin
    bl affichageMess

100:                                 @ standard end of the program
    mov r0, #0                       @ return code
    mov r7, #EXIT                    @ request to exit program
    svc #0                           @ perform the system call

iAdrsMessValeur:          .int sMessValeur
iAdrszCarriageReturn:     .int szCarriageReturn
iAdrsMessResult:          .int sMessResult
iAdribox:                 .int ibox
iAdrszMessGameWin:        .int szMessGameWin
iAdrsMessCounter:         .int sMessCounter
/******************************************************************/
/*     key move                                                   */
/******************************************************************/
/* r0 contains boxs address           */
/* r1 contains key value               */
/* r9 move counter                     */
/* r10 contains location empty box    */
keyMove:
    push {r1-r8,lr}                  @ save  registers
    mov r8,r0
    cmp r1,#0x42                     @ down arrow
    bne 1f
    cmp r10,#4                       @ if r10 < 4   error
    blt 80f
    sub r2,r10,#4                    @ compute location
    b 90f
1:
    cmp r1,#0x41                     @ high arrow
    bne 2f
    cmp r10,#11                      @ if r10 > 11   error
    bgt 80f
    add r2,r10,#4                    @ compute location
    b 90f
2:
    cmp r1,#0x43                     @ right arrow
    bne 3f
    tst r10,#0b11                    @ if r10 = 0,4,8,12   error
    beq 80f
    sub r2,r10,#1                    @ compute location
    b 90f
3:
    cmp r1,#0x44                     @ left arrow
    bne 100f
    and r3,r10,#0b11                 @ error if r10 = 3 7 11 and 15
    cmp r3,#3
    beq 80f
    add r2,r10,#1                    @ compute location
    b 90f

80:                                  @ move error
    ldr r0,iAdriCodeError
    mov r1,#1
    str r1,[r0]
    b 100f
90:                                  @ white box and move box inversion
    ldr r3,[r8,r2,lsl #2]
    str r3,[r8,r10,lsl #2]
    mov r10,r2
    mov r3,#0
    str r3,[r8,r10,lsl #2]
    add r9,#1                        @ increment move counter
100:
    pop {r1-r8,lr}                   @ restaur registers
    bx lr                            @return
iAdriCodeError:             .int iCodeError
/******************************************************************/
/*     shuffle game                                       */
/******************************************************************/
/* r0 contains boxs address           */
shuffleGame:
    push {r1-r6,lr}                     @ save  registers
    mov r1,r0
    mov r0,#4
    bl genereraleas
    lsl r4,r0,#1
    mov r0,r8
1:
    mov r0,#14
    bl genereraleas
    add r6,r0,#1
    mov r0,#14
    bl genereraleas
    add r5,r0,#1
    ldr r2,[r1,r6,lsl #2]
    ldr r3,[r1,r5,lsl #2]
    str r2,[r1,r5,lsl #2]
    str r3,[r1,r6,lsl #2]
    subs r4,#1
    bgt 1b

100:
    pop {r1-r6,lr}                      @ restaur registers
    bx lr                               @return
/******************************************************************/
/*     game Ok ?                                      */
/******************************************************************/
/* r0 contains boxs address           */
gameOK:
    push {r1-r8,lr}                     @ save  registers
    mov r8,r0
    mov r2,#0
    ldr r3,[r8,r2,lsl #2]
    add r2,#1
1:
    ldr r4,[r8,r2,lsl #2]
    cmp r4,r3
    movlt r0,#0                         @ game mot Ok
    blt 100f
    mov r3,r4
    add r2,#1
    cmp r2,#NBBOX -2
    ble 1b
    mov r0,#1                           @ game Ok

100:
    pop {r1-r8,lr}                      @ restaur registers
    bx lr                               @return
/******************************************************************/
/*     display game                                       */
/******************************************************************/
/* r0 contains boxs address           */
displayGame:
    push {r1-r5,lr}                     @ save  registers
    @ clear !
    mov r4,r0
    ldr r0,iAdrszClear
    bl affichageMess
    mov r2,#0
    ldr r1,iAdrsMessValeur
1:
    ldr r0,[r4,r2,lsl #2]
    cmp r0,#0
    ldreq r0,iSpaces                    @ store spaces
    streq r0,[r1]
    beq 2f
    bl conversion10                     @ call conversion decimal
    mov r0,#0
    strb r0,[r1,#3]                     @ zéro final
2:

    ldr r0,iAdrsMessResult
    bl affichageMess                    @ display message
    add r0,r2,#1
    tst r0,#0b11
    bne 3f
    ldr r0,iAdrszCarriageReturn
    bl affichageMess                    @ display message
3:
    add r2,#1
    cmp r2,#NBBOX - 1
    ble 1b
    ldr r0,iAdrszCarriageReturn
    bl affichageMess                    @ display line return
    ldr r0,iAdriCodeError               @ error detected ?
    ldr r1,[r0]
    cmp r1,#0
    beq 100f
    mov r1,#0                           @ raz error code
    str r1,[r0]
    ldr r0,iAdrszMessMoveError          @ display error message
    bl affichageMess
100:
    pop {r1-r5,lr}                      @ restaur registers
    bx lr                               @return
iSpaces:                       .int 0x00202020       @ spaces
iAdrszClear:                   .int szClear
iAdrszMessMoveError:           .int szMessMoveError
/******************************************************************/
/*     display text with size calculation                         */
/******************************************************************/
/* r0 contains the address of the message */
affichageMess:
    push {r0,r1,r2,r7,lr}                          @ save  registres
    mov r2,#0                                      @ counter length
1:                                                 @ loop length calculation
    ldrb r1,[r0,r2]                                @ read octet start position + index
    cmp r1,#0                                      @ if 0 its over
    addne r2,r2,#1                                 @ else add 1 in the length
    bne 1b                                         @ and loop
                                                   @ so here r2 contains the length of the message
    mov r1,r0                                      @ address message in r1
    mov r0,#STDOUT                                 @ code to write to the standard output Linux
    mov r7, #WRITE                                 @ code call system "write"
    svc #0                                         @ call systeme
    pop {r0,r1,r2,r7,lr}                           @ restaur des  2 registres */
    bx lr                                          @ return
/******************************************************************/
/*     Converting a register to a decimal unsigned                */
/******************************************************************/
/* r0 contains value and r1 address area   */
/* r0 return size of result (no zero final in area) */
/* area size => 11 bytes          */
.equ LGZONECAL,   10
conversion10:
    push {r1-r4,lr}                                 @ save registers
    mov r3,r1
    mov r2,#LGZONECAL
1:                                                  @ start loop
    bl divisionpar10U                               @ unsigned  r0 <- dividende. quotient ->r0 reste -> r1
    add r1,#48                                      @ digit
    strb r1,[r3,r2]                                 @ store digit on area
    cmp r0,#0                                       @ stop if quotient = 0
    subne r2,#1                                     @ else previous position
    bne 1b                                          @ and loop
                                                    @ and move digit from left of area
    mov r4,#0
2:
    ldrb r1,[r3,r2]
    strb r1,[r3,r4]
    add r2,#1
    add r4,#1
    cmp r2,#LGZONECAL
    ble 2b
                                                      @ and move spaces in end on area
    mov r0,r4                                         @ result length
    mov r1,#' '                                       @ space
3:
    strb r1,[r3,r4]                                   @ store space in area
    add r4,#1                                         @ next position
    cmp r4,#LGZONECAL
    ble 3b                                            @ loop if r4 <= area size

100:
    pop {r1-r4,lr}                                    @ restaur registres
    bx lr                                             @return

/***************************************************/
/*   division par 10   unsigned                    */
/***************************************************/
/* r0 dividende   */
/* r0 quotient    */
/* r1 remainder   */
divisionpar10U:
    push {r2,r3,r4, lr}
    mov r4,r0                                          @ save value
    ldr r3,iMagicNumber                                @ r3 <- magic_number    raspberry 1 2
    umull r1, r2, r3, r0                               @ r1<- Lower32Bits(r1*r0) r2<- Upper32Bits(r1*r0)
    mov r0, r2, LSR #3                                 @ r2 <- r2 >> shift 3
    add r2,r0,r0, lsl #2                               @ r2 <- r0 * 5
    sub r1,r4,r2, lsl #1                               @ r1 <- r4 - (r2 * 2)  = r4 - (r0 * 10)
    pop {r2,r3,r4,lr}
    bx lr                                              @ leave function
iMagicNumber:  	.int 0xCCCCCCCD
/***************************************************/
/*   Generation random number                  */
/***************************************************/
/* r0 contains limit  */
genereraleas:
    push {r1-r4,lr}                                    @ save registers
    ldr r4,iAdriGraine
    ldr r2,[r4]
    ldr r3,iNbDep1
    mul r2,r3,r2
    ldr r3,iNbDep1
    add r2,r2,r3
    str r2,[r4]                                        @ maj de la graine pour l appel suivant
    cmp r0,#0
    beq 100f
    mov r1,r0                                          @ divisor
    mov r0,r2                                          @ dividende
    bl division
    mov r0,r3                                          @ résult = remainder

100:                                                   @ end function
    pop {r1-r4,lr}                                     @ restaur registers
    bx lr                                              @ return
/*****************************************************/
iAdriGraine: .int iGraine
iNbDep1: .int 0x343FD
iNbDep2: .int 0x269EC3
/***************************************************/
/* integer division unsigned                       */
/***************************************************/
division:
    /* r0 contains dividend */
    /* r1 contains divisor */
    /* r2 returns quotient */
    /* r3 returns remainder */
    push {r4, lr}
    mov r2, #0                                         @ init quotient
    mov r3, #0                                         @ init remainder
    mov r4, #32                                        @ init counter bits
    b 2f
1:                                                     @ loop
    movs r0, r0, LSL #1                                @ r0 <- r0 << 1 updating cpsr (sets C if 31st bit of r0 was 1)
    adc r3, r3, r3                                     @ r3 <- r3 + r3 + C. This is equivalent to r3 ? (r3 << 1) + C
    cmp r3, r1                                         @ compute r3 - r1 and update cpsr
    subhs r3, r3, r1                                   @ if r3 >= r1 (C=1) then r3 <- r3 - r1
    adc r2, r2, r2                                     @ r2 <- r2 + r2 + C. This is equivalent to r2 <- (r2 << 1) + C
2:
    subs r4, r4, #1                                    @ r4 <- r4 - 1
    bpl 1b                                             @ if r4 >= 0 (N=0) then loop
    pop {r4, lr}
    bx lr
/***************************************************/
/* read touch                                      */
/***************************************************/
readKey:
    push {r1-r7,lr}
    mov r5,#0
    /* read terminal state */
    mov r0,#STDIN                                @ input console
    mov r1,#TCGETS
    ldr r2,iAdrstOldtio
    mov r7, #IOCTL                               @ call system Linux
    svc #0
    cmp r0,#0                                    @ error ?
    beq 1f
    ldr r1,iAdrszMessErreur                      @ error message
    bl   displayError
    mov r0,#-1
    b 100f
1:
    adr r0,sighandler                            @ adresse routine traitement signal
    ldr r1,iAdrstSigAction                       @ adresse structure sigaction
    str r0,[r1,#sa_handler]                      @ maj handler
    mov r0,#SIGINT                               @ signal type
    ldr r1,iAdrstSigAction
    mov r2,#0                                    @ NULL
    mov r7, #SIGACTION                           @ call system
    svc #0
    cmp r0,#0                                    @ error ?
    bne 97f
    mov r0,#SIGQUIT
    ldr r1,iAdrstSigAction
    mov r2,#0                                    @ NULL
    mov r7, #SIGACTION                           @ call system
    svc #0
    cmp r0,#0                                    @ error ?
    bne 97f
    mov r0,#SIGTERM
    ldr r1,iAdrstSigAction
    mov r2,#0                                    @ NULL
    mov r7, #SIGACTION                           @ appel systeme
    svc #0
    cmp r0,#0
    bne 97f
    @
    adr r0,iSIG_IGN                              @ address signal ignore function
    ldr r1,iAdrstSigAction1
    str r0,[r1,#sa_handler]
    mov r0,#SIGTTOU                              @invalidate other process signal
    ldr r1,iAdrstSigAction1
    mov r2,#0                                    @ NULL
    mov r7,#SIGACTION                            @ call system
    svc #0
    cmp r0,#0
    bne 97f
    @
    /* read terminal current state  */
    mov r0,#STDIN
    mov r1,#TCGETS
    ldr r2,iAdrstCurtio                          @ address current termio
    mov r7,#IOCTL                                @ call systeme
    svc #0
    cmp r0,#0                                    @ error ?
    bne 97f
    mov r2,#ICANON | ECHO                        @ no key pressed echo on display
    mvn r2,r2                                    @ and one key
    ldr r1,iAdrstCurtio
    ldr r3,[r1,#term_c_lflag]
    and r3,r2                                    @ add flags
    str r3,[r1,#term_c_lflag]                    @ and store
    mov r0,#STDIN                                @ maj terminal current state
    mov r1,#TCSETS
    ldr r2,iAdrstCurtio
    mov r7, #IOCTL                               @ call system
    svc #0
    cmp r0,#0
    bne 97f
    @
2:                                               @ loop waiting key
    ldr r0,iAdriEnd                              @ if signal ctrl-c  -> end
    ldr r0,[r0]
    cmp r0,#0
    movne r5,#-1
    bne 98f
    ldr r0,iAdrstPoll1                            @ address structure poll
    mov r1,#STDIN
    str r1,[r0,#poll_fd]                          @ maj FD
    mov r1,#POLLIN                                @ action code
    str r1,[r0,#poll_events]
    mov r1,#1                                     @ items number structure poll
    mov r2,#0                                     @ timeout = 0
    mov r7,#SYSPOLL                               @ call system POLL
    svc #0
    cmp r0,#0                                     @ key pressed ?
    ble 2b                                        @ no key pressed -> loop
                                                  @ read key
    mov r0,#STDIN                                 @ File Descriptor
    ldr r1,iAdriTouche                            @ buffer address
    mov r2,#TAILLEBUFFER                          @ buffer size
    mov r7,#READ                                  @ read key
    svc #0
    cmp r0,#0                                     @ error ?
    bgt 98f

97:                                               @ error detected
    ldr r1,iAdrszMessErreur                       @ error message
    bl   displayError
    mov r5,#-1
98:                                               @ end then restaur begin state terminal
    mov r0,#STDIN
    mov r1,#TCSETS
    ldr r2,iAdrstOldtio
    mov r7,#IOCTL                                 @ call system
    svc #0
    cmp r0,#0
    beq 99f                                       @ restaur ok
    ldr r1,iAdrszMessErreur                       @ error message
    bl   displayError
    mov r0,#-1
    b 100f
99:
    cmp r5,#0                                     @ error or control-c
    ldreq r2,iAdriTouche                          @ key address
    ldreqb r0,[r2,#2]                             @ return key byte
    movne r0,r5                                   @ or error
100:
    pop {r1-r7, lr}
    bx lr
iSIG_IGN:                 .int 1
iAdriEnd:                 .int iEnd
iAdrstPoll1:              .int stPoll1
iAdriTouche:              .int iTouche
iAdrstOldtio:             .int stOldtio
iAdrstCurtio:             .int stCurtio
iAdrstSigAction:          .int stSigAction
iAdrstSigAction1:         .int stSigAction1
iAdrszMessErreur :        .int szMessErreur
/******************************************************************/
/*     traitement du signal                                       */
/******************************************************************/
sighandler:
    push {r0,r1}
    ldr r0,iAdriEnd
    mov r1,#1                 @ maj zone end
    str r1,[r0]
    pop {r0,r1}
    bx lr
/***************************************************/
/*   display error message                         */
/***************************************************/
/* r0 contains error code  r1 : message address */
displayError:
    push {r0-r2,lr}                         @ save registers
    mov r2,r0                               @ save error code
    mov r0,r1
    bl affichageMess
    mov r0,r2                               @ error code
    ldr r1,iAdrsHexa
    bl conversion16                         @ conversion hexa
    mov r0,r2                               @ error code
    ldr r1,iAdrsDeci                        @ result address
    bl conversion10                         @ conversion decimale
    ldr r0,iAdrszMessErr                    @ display error message
    bl affichageMess
100:
    pop {r0-r2,lr}                          @ restaur registers
    bx lr                                   @ return
iAdrszMessErr:                 .int szMessErr
iAdrsHexa:                     .int sHexa
iAdrsDeci:                     .int sDeci
/******************************************************************/
/*     Converting a register to hexadecimal                      */
/******************************************************************/
/* r0 contains value and r1 address area   */
conversion16:
    push {r1-r4,lr}                                    @ save registers
    mov r2,#28                                         @ start bit position
    mov r4,#0xF0000000                                 @ mask
    mov r3,r0                                          @ save entry value
1:                                                     @ start loop
    and r0,r3,r4                                       @value register and mask
    lsr r0,r2                                          @ move right
    cmp r0,#10                                         @ compare value
    addlt r0,#48                                       @ <10  ->digit
    addge r0,#55                                       @ >10  ->letter A-F
    strb r0,[r1],#1                                    @ store digit on area and + 1 in area address
    lsr r4,#4                                          @ shift mask 4 positions
    subs r2,#4                                         @  counter bits - 4 <= zero  ?
    bge 1b                                             @  no -> loop

100:
    pop {r1-r4,lr}                                     @ restaur registers
    bx lr                                              @return


type Puzzle(var items: {}, var position: -1)

fun mainframe(puz):
    let d = puz.items
    print('+-----+-----+-----+-----+')
    print(d[1], d[2], d[3], d[4], first: '|', sep: '|', last: '|')
    print('+-----+-----+-----+-----+')
    print(d[5], d[6], d[7], d[8], first: '|', sep: '|', last: '|')
    print('+-----+-----+-----+-----+')
    print(d[9], d[10], d[11], d[12], first: '|', sep: '|', last: '|')
    print('+-----+-----+-----+-----+')
    print(d[13], d[14], d[15], d[16], first: '|', sep: '|', last: '|')
    print('+-----+-----+-----+-----+')

fun format(puz, ch):
    match ch.trim().length:
        1 => '  $ch  '
        2 => '  $ch '
        0 => '     '

fun change(puz, to):
    let fro = puz.position
    for a, b in puz.items where b == puz.format(str i):
        to = a
        break

    swap(puz.items[fro], :[to])
    puz.position = to;

fun buildboard(puz, difficulty):
    for i in 1..16:
        puz.items[i] = puz.format(str i)

    var tmp = a
    for a, b in puz.items where b == '  16 ':
        puz.items[a] = '     '
            tmp = a
            break

    puz.position = tmp
    let diff = match difficulty:
        0 => 10
        1 => 50
        _ => 100

    for i in 1..diff:
        let lst = puz.validmoves()
        let lst1 = []
        for j in lst:
            lst1.push! j.trim().int()
        puz.change(lst1[random(1, lst1.length - 1)])

fun validmoves(puz):
    match puz.position:
        6 | 7 | 10 | 11 =>
            puz.items[pos - 4], :[pos - 1], :[pos + 1], :[pos + 4]
        5 | 9 =>
            puz.items[pos - 4], :[pos + 4], :[pos + 1]
        8 | 12 =>
            puz.items[pos - 4], :[pos + 4], :[pos - 1]
        2 | 3 =>
            puz.items[pos - 1], :[pos + 1], :[pos + 4]
        14 | 15 =>
            puz.items[pos - 1], :[pos + 1], :[pos - 4]
        1 =>
            puz.items[pos + 1], :[pos + 4]
        4 =>
            puz.items[pos - 1], :[pos + 4]
        13 =>
            puz.items[pos + 1], :[pos - 4]
        16 =>
            puz.items[pos - 1], :[pos - 4]

fun mainframe(puz):
    var flag = false
    for a, b in puz.items:
        if b == '     ':
            pass
        else:
            flag = (a == b.trim().int())
    ..
    return flag

let game = Puzzle()
game.buildboard(
    int(input('Enter the difficulty : 0 1 2\n2 => highest 0=> lowest\n'))
)
game.mainframe()

print 'Enter 0 to exit'

loop:
    print 'Hello user:\nTo change the position just enter the no. near it'

    var lst = game.validmoves()
    var lst1 = []
    for i in lst:
        lst1.push! i.trim().int()
        print(i.strip(), '\t', last: '')

    print()

    let value = int(input())
    if value == 0:
        break
    elif x not in lst1:
        print('Wrong move')
    else:
        game.change(x)

    game.mainframe()
    if g.gameover():
        print 'You WON'
        break


Size := 20
Grid := [], Deltas := ["-1,0","1,0","0,-1","0,1"], Width := Size * 2.5
Gui, font, S%Size%
Gui, add, text, y1
loop, 4
{
	Row := A_Index
	loop, 4
	{
		Col := A_Index
		Gui, add, button, % (Col=1 ? "xs y+1" : "x+1 yp") " v" Row "_" Col " w" Width " gButton -TabStop", % Grid[Row,Col] := Col + (Row-1)*4 ; 1-16
	}
}
GuiControl, Hide, % Row "_" Col	; 4_4
Gui, add, Button, % "xs gShuffle w" 4 * Width + 3, Shuffle
Gui, show,, 15 Puzzle
return
;------------------------------
GuiClose:
ExitApp
return
;------------------------------
Shuffle:
Shuffle := true
loop, 1000
{
	Random, Rnd, 1,4
	Move(StrSplit(Deltas[Rnd], ",").1, StrSplit(Deltas[Rnd], ",").2)
}
Shuffle := false
return
;------------------------------
Button:
buttonRow := SubStr(A_GuiControl, 1, 1), ButtonCol := SubStr(A_GuiControl, 3, 1)
if Abs(buttonRow-Row) > 1 || Abs(ButtonCol-Col) > 1 || Abs(buttonRow-Row) = Abs(ButtonCol-Col)
	return
Move(buttonRow-Row, ButtonCol-Col)
return
;------------------------------
#IfWinActive, 15 Puzzle
;------------------------------
Down::
Move(-1, 0)
return
;------------------------------
Up::
Move(1, 0)
return
;------------------------------
Right::
Move(0, -1)
return
;------------------------------
Left::
Move(0, 1)
return
;------------------------------
#IfWinActive
;------------------------------
Move(deltaRow, deltaCol){
	global
	if (Row+deltaRow=0) || (Row+deltaRow=5) || (Col+deltaCol=0) || (Col+deltaCol=5)
		return
	GuiControl, Hide, % Row+deltaRow "_" Col+deltaCol
	GuiControl, Show, % Row "_" Col
	GuiControl,, %Row%_%Col%, % Grid[Row+deltaRow, Col+deltaCol]
	Grid[Row, Col] := Grid[Row+deltaRow, Col+deltaCol]
	Grid[Row+=deltaRow, Col+=deltaCol] := 16
	if Shuffle
		return
	gridCont := ""
	for m, obj in grid
		for n, val in obj
			gridCont .= val ","
	if (Trim(gridCont, ",") = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16")
		MsgBox, 262208, 15 Puzzle, You solved 15 Puzzle
}


      IF INKEY(-256)=77 OR (INKEY(-256) AND &F0)=&A0 THEN MODE 1: COLOUR 0: COLOUR 143: *FX4,1

      SIZE=4 : DIFFICULTY=3

      MAX=SIZE * SIZE - 1
      DIM Board(MAX)
      FOR I%=1 TO MAX : Board(I% - 1)=I% : NEXT
      Gap=MAX
      WHILE N% < DIFFICULTY ^ 2 PROCSlide(RND(4)) : ENDWHILE : REM Shuffle
      N%=0

      @%=2 + LOG(MAX + 1)
      PROCShowAndTest
      WHILE NOT Solved
        PRINT "Use arrow keys to move the gap around. Moves taken: ";N%
        PROCSlide(GET - 135)
        PROCShowAndTest
      ENDWHILE
      PRINT "Solved after ";N% LEFT$(" moves", 6 + (N% = 1)) "."
      END

      DEF PROCSlide(dir%)
      NewGap=Gap
      CASE dir% OF
        WHEN 1: IF Gap MOD SIZE > 0        NewGap=Gap - 1    : N%+=1 : REM Left
        WHEN 2: IF Gap MOD SIZE < SIZE - 1 NewGap=Gap + 1    : N%+=1 : REM Right
        WHEN 3: IF Gap < MAX - SIZE + 1    NewGap=Gap + SIZE : N%+=1 : REM Down
        WHEN 4: IF Gap > SIZE - 1          NewGap=Gap - SIZE : N%+=1 : REM Up
      ENDCASE
      SWAP Board(Gap), Board(NewGap)
      Gap=NewGap
      ENDPROC

      DEF PROCShowAndTest
      CLS
      Solved=TRUE
      FOR I%=0 TO MAX
        COLOUR 12 : COLOUR 135
        IF I% = Gap COLOUR 1 : COLOUR 129
        IF I% MOD SIZE = SIZE - 1 PRINT Board(I%) ELSE PRINT Board(I%),;
        IF Solved IF I% < MAX - 1 IF Board(I%) > Board(I% + 1) OR I% = Gap Solved=FALSE
      NEXT
      COLOUR 0 : COLOUR 143
      PRINT
      ENDPROC


_while_ ← {𝔽⍟𝔾∘𝔽_𝕣_𝔾∘𝔽⍟𝔾𝕩}
FPG←{
  𝕊𝕩: 4‿4𝕊𝕩;
  (∧´𝕨<0)∨2≠≠𝕨 ? •Out "Invalid shape: "∾•Fmt 𝕨;
  0≠=𝕩 ? •Out "Invalid shuffle count: "∾•Fmt 𝕩;
  s𝕊𝕩:
  d←⟨1‿0⋄¯1‿0⋄0‿1⋄0‿¯1⟩ # Directions
  w←𝕨⥊1⌽↕×´𝕨 # Solved grid
  b←w        # Board
  z←⊑{
    z‿p←𝕩
    p↩(⊢≡s⊸|)¨⊸/(<z)+d(¬∘∊/⊣)p # filter out invalid
    n←(•rand.Range ≠p)⊑p
    b⌽⌾(z‿n⊸⊑)↩ # switch places
    -`n‿z
  }⍟𝕩 ⟨𝕨-1,⟨0⟩⟩
  {
    𝕊:
    b≡w ? •Show b, •Out "You win", 0;
    •Show b
    inp←⊑{
      Check 𝕩:
      •Out "Enter move: "
      x←•[email protected]
      i←⊑"↑↓←→q"⊐x
      {
        i=4 ? i; # quit
        i>4 ? •Out "Invalid direction: "∾x, Check x;
        (⊢≢s⊸|)z+i⊑d ? •Out "Out of bounds: "∾x, Check x;
        i
      }
    } @
    {
      𝕩=4 ? •Out "Quitting", 0;
      mv←z+𝕩⊑d
      b⌽⌾(mv‿z⊸⊑)↩
      z↩mv
      1
    } inp
  } _while_ ⊢ 1
  @
}


/* RosettaCode: Fifteen puzle game, C89, plain vanillia TTY, MVC, § 22 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 4
#define M 4
enum Move{UP,DOWN,LEFT,RIGHT};int hR;int hC;int cc[N][M];const int nS=100;int
update(enum Move m){const int dx[]={0,0,-1,1};const int dy[]={-1,1,0,0};int i=hR
+dy[m];int j=hC+dx[m];if(i>= 0&&i<N&&j>=0&&j<M){cc[hR][hC]=cc[i][j];cc[i][j]=0;
hR=i;hC=j;return 1;}return 0;}void setup(void){int i,j,k;for(i=0;i<N;i++)for(j=0
;j<M;j++)cc[i][j]=i*M+j+1;cc[N-1][M-1]=0;hR=N-1;hC=M-1;k=0;while(k<nS)k+=update(
(enum Move)(rand()%4));}int isEnd(void){int i,j; int k=1;for(i=0;i<N;i++)for(j=0
;j<M;j++)if((k<N*M)&&(cc[i][j]!=k++))return 0;return 1;}void show(){int i,j;
putchar('\n');for(i=0;i<N;i++)for(j=0;j<M;j++){if(cc[i][j])printf(j!=M-1?" %2d "
:" %2d \n",cc[i][j]);else printf(j!=M-1?" %2s ":" %2s \n", "");}putchar('\n');}
void disp(char* s){printf("\n%s\n", s);}enum Move get(void){int c;for(;;){printf
("%s","enter u/d/l/r : ");c=getchar();while(getchar()!='\n');switch(c){case 27:
exit(0);case'd':return UP;case'u':return DOWN;case'r':return LEFT;case'l':return
RIGHT;}}}void pause(void){getchar();}int main(void){srand((unsigned)time(NULL));
do setup();while(isEnd());show();while(!isEnd()){update(get());show();}disp(
"You win"); pause();return 0;}


using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

public class FifteenPuzzle
{
    const int gridSize = 4; //Standard 15 puzzle is 4x4
    const bool evenSized = gridSize % 2 == 0;
    const int blockCount = gridSize * gridSize;
    const int last = blockCount - 1;
    const int buttonSize = 50;
    const int buttonMargin = 3; //default = 3
    const int formEdge = 9;
    static readonly Random rnd = new Random();
    static readonly Font buttonFont = new Font("Arial", 15.75F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
    readonly Button[] buttons = new Button[blockCount];
    readonly int[] grid = new int[blockCount];
    readonly int[] positionOf = new int[blockCount];
    int moves = 0;
    DateTime start;

    public static void Main(string[] args)
    {
        FifteenPuzzle p = new FifteenPuzzle();
        Form f = p.BuildForm();
        Application.Run(f);
    }

    public FifteenPuzzle()
    {
        for (int i = 0; i < blockCount; i++) {
            grid[i] = i;
            positionOf[i] = i;
        }
    }

    Form BuildForm()
    {
        Button startButton = new Button {
            Font = new Font("Arial", 9.75F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0))),
            Size = new Size(86, 23),
            Location = new Point(formEdge,
                (buttonSize + buttonMargin * 2) * gridSize + buttonMargin + formEdge),
            Text = "New Game",
            UseVisualStyleBackColor = true
        };
        startButton.Click += (sender, e) => Shuffle();

        int size = buttonSize * gridSize + buttonMargin * gridSize * 2 + formEdge * 2;
        Form form = new Form {
            Text = "Fifteen",
            ClientSize = new Size(width: size, height: size + buttonMargin * 2 + startButton.Height)
        };
        form.SuspendLayout();
        for (int index = 0; index < blockCount; index++) {
            Button button = new Button {
                Font = buttonFont,
                Size = new Size(buttonSize, buttonSize),
                //Margin = new Padding(buttonMargin),
                Text = (index + 1).ToString(),
                UseVisualStyleBackColor = true
            };
            SetLocation(button, index);
            form.Controls.Add(button);
            buttons[index] = button;
            int i = index;
            button.Click += (sender, e) => ButtonClick(i);
        }
        form.Controls.Add(startButton);
        form.ResumeLayout();
        return form;
    }

    void ButtonClick(int i)
    {
        if (buttons[last].Visible) return;
        int target = positionOf[i];
        if (positionOf[i] / gridSize == positionOf[last] / gridSize) {
            while (positionOf[last] < target) {
                Swap(last, grid[positionOf[last] + 1]);
                moves++;
            }
            while (positionOf[last] > target) {
                Swap(last, grid[positionOf[last] - 1]);
                moves++;
            }
        } else if (positionOf[i] % gridSize == positionOf[last] % gridSize) {
            while (positionOf[last] < target) {
                Swap(last, grid[positionOf[last] + gridSize]);
                moves++;
            }
            while (positionOf[last] > target) {
                Swap(last, grid[positionOf[last] - gridSize]);
                moves++;
            }
        }
        if (Solved()) {
            TimeSpan elapsed = DateTime.Now - start;
            elapsed = TimeSpan.FromSeconds(Math.Round(elapsed.TotalSeconds, 0));
            buttons[last].Visible = true;
            MessageBox.Show($"Solved in {moves} moves. Time: {elapsed}");
        }
    }

    bool Solved() => Enumerable.Range(0, blockCount - 1).All(i => positionOf[i] == i);

    static void SetLocation(Button button, int index)
    {
        int row = index / gridSize, column = index % gridSize;
        button.Location = new Point(
            (buttonSize + buttonMargin * 2) * column + buttonMargin + formEdge,
            (buttonSize + buttonMargin * 2) * row + buttonMargin + formEdge);
    }

    void Shuffle()
    {
        for (int i = 0; i < blockCount; i++) {
            int r = rnd.Next(i, blockCount);
            int g = grid[r];
            grid[r] = grid[i];
            grid[i] = g;
        }
        for (int i = 0; i < blockCount; i++) {
            positionOf[grid[i]] = i;
            SetLocation(buttons[grid[i]], i);
        }
        if (!Solvable()) Swap(0, 1); //Swap any 2 blocks

        buttons[last].Visible = false;
        moves = 0;
        start = DateTime.Now;
    }

    bool Solvable()
    {
        bool parity = true;
        for (int i = 0; i < blockCount - 2; i++) {
            for (int j = i + 1; j < blockCount - 1; j++) {
                if (positionOf[j] < positionOf[i]) parity = !parity;
            }
        }
        if (evenSized && positionOf[last] / gridSize % 2 == 0) parity = !parity;
        return parity;
    }

    void Swap(int a, int b)
    {
        Point location = buttons[a].Location;
        buttons[a].Location = buttons[b].Location;
        buttons[b].Location = location;

        int p = positionOf[a];
        positionOf[a] = positionOf[b];
        positionOf[b] = p;

        grid[positionOf[a]] = a;
        grid[positionOf[b]] = b;
    }
}


#include <time.h>
#include <stdlib.h>
#include <vector>
#include <string>
#include <iostream>
class p15 {
public :
    void play() {
        bool p = true;
        std::string a;
        while( p ) {
            createBrd();
            while( !isDone() ) { drawBrd();getMove(); }
            drawBrd();
            std::cout << "\n\nCongratulations!\nPlay again (Y/N)?";
            std::cin >> a; if( a != "Y" && a != "y" ) break;
        }
    }
private:
    void createBrd() {
        int i = 1; std::vector<int> v;
        for( ; i < 16; i++ ) { brd[i - 1] = i; }
        brd[15] = 0; x = y = 3;
        for( i = 0; i < 1000; i++ ) {
            getCandidates( v );
            move( v[rand() % v.size()] );
            v.clear();
        }
    }
    void move( int d ) {
        int t = x + y * 4;
        switch( d ) {
            case 1: y--; break;
            case 2: x++; break;
            case 4: y++; break;
            case 8: x--;
        }
        brd[t] = brd[x + y * 4];
        brd[x + y * 4] = 0;
    }
    void getCandidates( std::vector<int>& v ) {
        if( x < 3 ) v.push_back( 2 ); if( x > 0 ) v.push_back( 8 );
        if( y < 3 ) v.push_back( 4 ); if( y > 0 ) v.push_back( 1 );
    }
    void drawBrd() {
        int r; std::cout << "\n\n";
        for( int y = 0; y < 4; y++ ) {
            std::cout << "+----+----+----+----+\n";
            for( int x = 0; x < 4; x++ ) {
                r = brd[x + y * 4];
                std::cout << "| ";
                if( r < 10 ) std::cout << " ";
                if( !r ) std::cout << "  ";
                else std::cout << r << " ";
            }
            std::cout << "|\n";
        }
        std::cout << "+----+----+----+----+\n";
    }
    void getMove() {
        std::vector<int> v; getCandidates( v );
        std::vector<int> p; getTiles( p, v ); unsigned int i;
        while( true ) {
            std::cout << "\nPossible moves: ";
            for( i = 0; i < p.size(); i++ ) std::cout << p[i] << " ";
            int z; std::cin >> z;
            for( i = 0; i < p.size(); i++ )
                if( z == p[i] ) { move( v[i] ); return; }
        }
    }
    void getTiles( std::vector<int>& p, std::vector<int>& v ) {
        for( unsigned int t = 0; t < v.size(); t++ ) {
            int xx = x, yy = y;
            switch( v[t] ) {
                case 1: yy--; break;
                case 2: xx++; break;
                case 4: yy++; break;
                case 8: xx--;
            }
            p.push_back( brd[xx + yy * 4] );
        }
    }
    bool isDone() {
        for( int i = 0; i < 15; i++ ) {
            if( brd[i] != i + 1 ) return false;
        }
        return true;
    }
    int brd[16], x, y;
};
int main( int argc, char* argv[] ) {
    srand( ( unsigned )time( 0 ) );
    p15 p; p.play(); return 0;
}


         >>SOURCE FORMAT FREE
*> This code is dedicated to the public domain
*> This is GNUCOBOL 2.0
identification division.
program-id. fifteen.
environment division.
configuration section.
repository. function all intrinsic.
data division.
working-storage section.

01  r pic 9.
01  r-empty pic 9.
01  r-to pic 9.
01  r-from pic 9.

01  c pic 9.
01  c-empty pic 9.
01  c-to pic 9.
01  c-from pic 9.

01  display-table.
    03  display-row occurs 4.
        05  display-cell occurs 4 pic 99.

01  tile-number pic 99.
01  tile-flags pic x(16).

01  display-move value spaces.
    03  tile-id pic 99.

01  row-separator pic x(21) value all '.'.
01  column-separator pic x(3) value ' . '.

01  inversions pic 99.
01  current-tile pic 99.

01  winning-display pic x(32) value
        '01020304'
    &   '05060708'
    &   '09101112'
    &   '13141500'.

procedure division.
start-fifteen.
    display 'start fifteen puzzle'
    display '    enter a two-digit tile number and press <enter> to move'
    display '    press <enter> only to exit'

    *> tables with an odd number of inversions are not solvable
    perform initialize-table with test after until inversions = 0
    perform show-table
    accept display-move
    perform until display-move = spaces
        perform move-tile
        perform show-table
        move spaces to display-move
        accept display-move
    end-perform
    stop run
    .
initialize-table.
    compute tile-number = random(seconds-past-midnight) *> seed only
    move spaces to tile-flags
    move 0 to current-tile inversions
    perform varying r from 1 by 1 until r > 4
    after c from 1 by 1 until c > 4
        perform with test after
        until tile-flags(tile-number + 1:1) = space
            compute tile-number = random() * 100
            compute tile-number = mod(tile-number, 16)
        end-perform
        move 'x' to tile-flags(tile-number + 1:1)
        if tile-number > 0 and < current-tile
            add 1 to inversions
        end-if
        move tile-number to display-cell(r,c) current-tile
    end-perform
    compute inversions = mod(inversions,2)
    .
show-table.
    if display-table = winning-display
        display 'winning'
    end-if
    display space row-separator
    perform varying r from 1 by 1 until r > 4
        perform varying c from 1 by 1 until c > 4
            display column-separator with no advancing
            if display-cell(r,c) = 00
                display '  ' with no advancing
                move r to r-empty
                move c to c-empty
            else
                display display-cell(r,c) with no advancing
            end-if
        end-perform
        display column-separator
    end-perform
    display space row-separator
    .
move-tile.
    if not (tile-id numeric and tile-id >= 01 and <= 15)
        display 'invalid tile number'
        exit paragraph
    end-if

    *> find the entered tile-id row and column (r,c)
    perform varying r from 1 by 1 until r > 4
    after c from 1 by 1 until c > 4
        if display-cell(r,c) = tile-id
            exit perform
        end-if
    end-perform

    *> show-table filled (r-empty,c-empty)
    evaluate true
    when r = r-empty
        if c-empty < c
            *> shift left
            perform varying c-to from c-empty by 1 until c-to > c
                compute c-from = c-to + 1
                move display-cell(r-empty,c-from) to display-cell(r-empty,c-to)
            end-perform
        else
           *> shift right
           perform varying c-to from c-empty by -1 until c-to < c
               compute c-from = c-to - 1
               move display-cell(r-empty,c-from) to display-cell(r-empty,c-to)
           end-perform
       end-if
       move 00 to display-cell(r,c)
    when c = c-empty
        if r-empty < r
            *>shift up
            perform varying r-to from r-empty by 1 until r-to > r
                compute r-from = r-to + 1
                move display-cell(r-from,c-empty) to display-cell(r-to,c-empty)
            end-perform
        else
            *> shift down
            perform varying r-to from r-empty by -1 until r-to < r
                compute r-from = r-to - 1
                move display-cell(r-from,c-empty) to display-cell(r-to,c-empty)
            end-perform
        end-if
        move 00 to display-cell(r,c)
    when other
         display 'invalid move'
    end-evaluate
    .
end program fifteen.


background 432
textsize 13
len f[] 16
func draw . .
  clear
  for i range 16
    h = f[i]
    if h < 16
      x = i mod 4 * 24 + 3
      y = i div 4 * 24 + 3
      color 210
      move x y
      rect 22 22
      move x + 4 y + 5
      if h < 10
        move x + 6 y + 5
      .
      color 885
      text h
    .
  .
.
global done .
func init . .
  done = 0
  for i range 16
    f[i] = i + 1
  .
  # shuffle
  for i = 14 downto 1
    r = random (i + 1)
    swap f[r] f[i]
  .
  # make it solvable
  inv = 0
  for i range 15
    for j range i
      if f[j] > f[i]
        inv += 1
      .
    .
  .
  if inv mod 2 <> 0
    swap f[0] f[1]
  .
  textsize 12
  call draw
.
func move_tile . .
  c = mouse_x div 25
  r = mouse_y div 25
  i = r * 4 + c
  if c > 0 and f[i - 1] = 16
    swap f[i] f[i - 1]
  elif r > 0 and f[i - 4] = 16
    swap f[i] f[i - 4]
  elif r < 3 and f[i + 4] = 16
    swap f[i] f[i + 4]
  elif c < 3 and f[i + 1] = 16
    swap f[i] f[i + 1]
  .
  call draw
  done = 1
  for i range 15
    if f[i] > f[i + 1]
      done = 0
    .
  .
  if done = 1
    clear
    move 10 30
    text "Well done!"
  .
.
on mouse_down
  if done = 1
    call init
  else
    call move_tile
  .
.
call init


// 15 Puzzle Game. Nigel Galloway: August 9th., 2020
let Nr,Nc,RA,rnd=[|3;0;0;0;0;1;1;1;1;2;2;2;2;3;3;3|],[|3;0;1;2;3;0;1;2;3;0;1;2;3;0;1;2|],[|for n in [1..16]->n%16|],System.Random()
let rec fN g Σ=function h::t->fN g (Σ+List.sumBy(fun n->if h>n then 1 else 0)t) t|_->(Σ-g/4)%2=1
let rec fI g=match if System.Console.IsInputRedirected then char(System.Console.Read()) else System.Console.ReadKey(true).KeyChar with
               n when Seq.contains n g->printf "%c" n; (match n with 'l'-> -1|'r'->1|'d'->4|_-> -4)|_->System.Console.Beep(); fI g
let rec fG n Σ=function 0->(List.findIndex((=)0)Σ,Σ)|g->let i=List.item(rnd.Next(g)) n in fG(List.except [i] n)(i::Σ)(g-1)
let rec fE()=let n,g=fG [0..15] [] 16 in if fN n 0 (List.except [0] g) then (n,Array.ofList g) else fE()
let rec fL(n,g) Σ=let fa=printfn "";Array.chunkBySize 4 g|>Array.iter(fun n->Array.iter(printf "%3d")n;printfn "")
                  match g=RA with true->printfn "Solved in %d moves" Σ; fa; 0
                                 |_->let vM=match n/4,n%4 with (0,0)->"rd"|(0,3)->"ld"|(0,_)->"lrd"|(3,0)->"ru"|(3,3)->"lu"|(3,_)->"lru"|(_,0)->"rud"|(_,3)->"lud"|_->"lrud"
                                     fa; printf "Move Number: %2d; Manhatten Distance: %2d; Choose move from %4s: " Σ
                                         ([0..15]|>List.sumBy(fun n->match g.[n] with 0->0 |i->(abs(n/4-Nr.[i]))+(abs(n%4-Nc.[i])))) vM
                                     let v=fI vM in g.[n]<-g.[n+v];g.[n+v]<-0;fL(n+v,g)(Σ+1)
[<EntryPoint>]
let main n = fL(match n with [|n|]->let g=[|let g=uint64 n in for n in 60..-4..0->int((g>>>n)&&&15UL)|] in (Array.findIndex((=)0)g,g) |_->fE()) 0


USING: accessors combinators combinators.extras
combinators.short-circuit grouping io kernel literals math
math.matrices math.order math.parser math.vectors prettyprint qw
random sequences sequences.extras ;
IN: rosetta-code.15-puzzle-game

<<

TUPLE: board matrix zero ;

: <board> ( -- board )
    16 <iota> 1 rotate 4 group { 3 3 } board boa ;

>>

CONSTANT: winning $[ <board> matrix>> ]

: input>dir ( str -- pair )
    {
        { "u" [ { 1 0 } ] }
        { "d" [ { -1 0 } ] }
        { "l" [ { 0 1 } ] }
        { "r" [ { 0 -1 } ] }
    } case ;

: get-index ( loc matrix -- elt ) [ first2 swap ] dip nth nth ;

: mexchange ( loc1 loc2 matrix -- )
    tuck [ [ [ get-index ] keepd ] [email protected] ] keep [ spin ] 2dip
    [ set-index ] keep set-index ;

: vclamp+ ( seq1 seq2 -- seq ) v+ { 0 0 } { 3 3 } vclamp ;

: slide-piece ( board str -- )
    over zero>> [ vclamp+ ] keep rot matrix>> mexchange ;

: move-zero ( board str -- )
    [ vclamp+ ] curry change-zero drop ;

: move ( board str -- )
    input>dir [ slide-piece ] [ move-zero ] 2bi ;

: rand-move ( board -- ) qw{ u d l r } random move ;

: shuffle-board ( board n -- board' ) [ dup rand-move ] times ;

: .board ( board -- ) matrix>> simple-table. ;

: get-input ( -- str )
    "Your move? (u/d/l/r/q) " write flush readln dup
    qw{ u d l r q } member? [ drop get-input ] unless ;

: won? ( board -- ? ) matrix>> winning = ;

DEFER: game
: process-input ( board -- board' )
    get-input dup "q" = [ drop ] [ game ] if ;

: check-win ( board -- board' )
    dup won? [ "You won!" print ] [ process-input ] if ;

: game ( board str -- board' )
    [ move ] keepd dup .board check-win ;

: valid-difficulty? ( obj -- ? )
    { [ fixnum? ] [ 3 100 between? ] } 1&& ;

: choose-difficulty ( -- n )
    "How many shuffles? (3-100) " write flush readln
    string>number dup valid-difficulty?
    [ drop choose-difficulty ] unless ;

: main ( -- )
    <board> choose-difficulty shuffle-board dup .board check-win
    drop ;

MAIN: main


#! /usr/bin/gforth

cell 8 <> [if] s" 64-bit system required" exception throw [then]

\ In the stack comments below,
\ "h" stands for the hole position (0..15),
\ "s" for a 64-bit integer representing a board state,
\ "t" a tile value (0..15, 0 is the hole),
\ "b" for a bit offset of a position within a state,
\ "m" for a masked value (4 bits selected out of a 64-bit state),
\ "w" for a weight of a current path,
\ "d" for a direction constant (0..3)

\ Utility
: 3dup   2 pick 2 pick 2 pick ;
: 4dup   2over 2over ;
: shift   dup 0 > if lshift else negate rshift then ;

hex 123456789abcdef0 decimal constant solution
: row   2 rshift ;   : col   3 and ;

: up-valid?    ( h -- f ) row 0 > ;
: down-valid?  ( h -- f ) row 3 < ;
: left-valid?  ( h -- f ) col 0 > ;
: right-valid? ( h -- f ) col 3 < ;

\ To iterate over all possible directions, put direction-related functions into arrays:
: ith ( u addr -- w ) swap cells + @ ;
create valid? ' up-valid? , ' left-valid? , ' right-valid? , ' down-valid? , does> ith execute ;
create step -4 , -1 , 1 , 4 , does> ith ;

\ Advance from a single state to another:
: bits ( h -- b ) 15 swap - 4 * ;
: tile ( s b -- t ) rshift 15 and ;
: new-state ( s h d -- s' ) step dup >r + bits 2dup tile ( s b t ) swap lshift tuck - swap r> 4 * shift + ;

: hole? ( s u -- f ) bits tile 0= ;
: hole ( s -- h ) 16 0 do dup i hole? if drop i unloop exit then loop drop ;

0 constant up 1 constant left 2 constant right 3 constant down

\ Print a board:
: .hole   space space space ;
: .tile ( u -- ) ?dup-0=-if .hole else dup 10 < if space then . then ;
: .board ( s -- ) 4 0 do cr 4 0 do dup j 4 * i + bits tile .tile loop loop drop ;
: .help   cr ." ijkl move, q quit" ;

\ Pseudorandom number generator:
create (rnd)   utime drop ,
: rnd   (rnd) @ dup 13 lshift xor dup 17 rshift xor dup dup 5 lshift xor (rnd) ! ;

: move ( s u -- s' ) >r dup hole r> new-state ;
: ?move ( s u -- s' ) >r dup hole [email protected] valid? if r> move else rdrop then ;
: shuffle ( s u -- s' ) 0 do rnd 3 and ?move loop ;

: win   cr ." you won!" bye ;
: turn ( s -- )
	page dup .board .help
	key case
		[char] q of bye endof
		[char] i of down ?move endof
		[char] j of right ?move endof
		[char] k of up ?move endof
		[char] l of left ?move endof
	endcase ;

: play  begin dup solution <> while turn repeat win ;

solution 1000 shuffle play


LOCZ = MINLOC(BOARD)	!Find the zero. 0 = BOARD(LOCZ(1),LOCZ(2)) == BOARD(ZC,ZR)