Code Puzzle Thread

Discuss Autohotkey related topics here. Not a place to share code.
Forum rules
Discuss Autohotkey related topics here. Not a place to share code.
Helgef
Posts: 3783
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

02 Feb 2019, 08:25

Hello jeeswg, I get multiple msgboxes indicating incorrect results in the test script, q::, eg

Code: Select all

put

index: 3

Float

5759439474786304.0

5759438937915392.0

0x14762E00000000

0x14762DE0000000
Cheers.
User avatar
jeeswg
Posts: 6471
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Code Puzzle Thread

02 Feb 2019, 08:48

- I posted this in the script:
note: when converting from/to Floats,
the fractions could be *fractionally* different,
although the tests show the expected accuracy
- You'll notice that the MsgBoxes only appear for Floats, and that the numbers match to around 7 significant figures.
- Maybe even for that I can find a better fix, but we're talking maths degree level stuff here, and an extensive time commitment.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 3783
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

03 Feb 2019, 15:36

the tests show the expected accuracy
It is not the correct result for recreating numput.
we're talking maths degree level stuff here
There is hardly any advanced math going on here. The bit manipulations are basic. Your approach requires knowledge about how numbers are stored, so this is not the way I expected the puzzle to be solved. The expected solution would demonstrate a knowledge about the AHK language, not about number formats. Your approach is acceptable, but currently not correctly implemented. I will give you a point for a good effort and good partial solution :clap: . I will give one point to whoever completes jeeswg's solution. I will give two points to whoever completes the full puzzle.

Cheers :wave: .
User avatar
jeeswg
Posts: 6471
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Code Puzzle Thread

12 Apr 2019, 15:12

I improved my script, and the MsgBox indicating a slight discrepancy when handling Floats/Doubles now appears less frequently. Around 0.7% of the time versus 24%.

Code: Select all

;slightly improved PuzPut function which handles rounding
;the MsgBox indicating a slight discrepancy now appears less frequently
;before: e.g. 2381/10000 = 0.2381 [24%]
;after: e.g. 67/10000 = 0.0067 [0.7%]

;AHK v2 script
;'PuzGet'/'PuzPut' - recreate NumGet/NumPut functionality
;where 'Puz' refers to 'Code Puzzle Thread'

;note: when converting from/to Floats,
;the fractions could be *fractionally* different,
;although the tests show the expected accuracy

;note: the maths of handling the raw binary data
;of the Floats is very fiddly, so errors are possible

;AHK v2 is used because you can convert from/to Doubles
;by using the address of a variable

w:: ;test PuzPut and PuzGet functions
VarSetCapacity(vData1, 8, 0)
VarSetCapacity(vData2, 8, 0)
NumPut(-12345678, &vData1, "Int")
PuzPut(-12345678, &vData2, "Int")
MsgBox(NumGet(&vData1, "Int"))
MsgBox(NumGet(&vData2, "Int"))
MsgBox(PuzGet(&vData1, "Int"))
return

q:: ;test PuzPut and PuzGet functions
;oType := StrSplit("Char,Short,Int,Int64,Ptr,UChar,UShort,UInt,UInt64,UPtr", ",")
;oType := StrSplit("Char,Short,Int,Int64,Ptr,UChar,UShort,UInt,UInt64,UPtr,Float,Double", ",")
oType := StrSplit("Float,Double", ",")
VarSetCapacity(vData1, 8)
VarSetCapacity(vData2, 8)
vCount := 0
Loop 10000
{
try
{
	vNum := Random(-2147483648, 2147483647)
	vNum *= 256**3
	vTemp := Random(1, oType.Length())
	vType := oType[vTemp]
	NumPut(vNum, &vData1, vType)
	PuzPut(vNum, &vData2, vType)
	vNum1 := NumGet(&vData1, vType)
	vNum2 := NumGet(&vData2, vType)
	vNum1X := PuzGet(&vData1, vType)
	vHex1 := Format("0x{:X}", vNum1)
	vHex2 := Format("0x{:X}", vNum2)
	vHex1X := Format("0x{:X}", vNum1X)
}
catch
	continue
;vCount++
	vIsErr := 0
	;if 0
	if !(vNum1 = vNum2)
	;if !InStr(vType, "Int64")
		MsgBox("put`r`n" "index: " A_Index "`r`n" vType "`r`n" vNum1 "`r`n" vNum2 "`r`n" vHex1 "`r`n" vHex2)
		, vIsErr := 1
	;if 0
	if !(vNum1 = vNum1X)
		MsgBox("get`r`n" "index: " A_Index "`r`n" vType "`r`n" vNum1 "`r`n" vNum1X "`r`n" vHex1 "`r`n" vHex1X)
		, vIsErr := 1
	if vIsErr
		vCount++
}
MsgBox("done " vCount)
return

;==================================================

PuzGet(vAddr, vType:="UPtr")
{
	local
	static oChar, oType := {Char:1, Short:2, Int:4, Int64:8, Ptr:A_PtrSize=8?8:4}
	static vIsReady := 0
	if !vIsReady
	{
		oChar := {0:0}
		VarSetCapacity(vData, 1, 0)
		Loop 255
			PuzPut(A_Index, &vData, "UChar")
			, vOrd := Ord(StrGet(&vData, 1, "CP0"))
			, oChar[vOrd] := A_Index
		vIsReady := 1
	}

	if (vType = "Double")
	{
		vData := 0.0
		memcpy(&vData, vAddr, 8)
		return vData
	}

	if (vType = "Float")
	{
		vNum := PuzGet(vAddr+0, "UInt")
		vSign := !!(vNum & 0x80000000)
		vPow := ((vNum & 0x7F800000) >> 23)
		vNum := vNum & 0x7FFFFF
		if (vPow = 255)
			vPow := 2047
		else if (vPow > 0)
			vPow := (vPow - 127 + 1023)
		;else if (vPow = 0)
		;	vPow := 0
		vNum <<= (52-23)
		vTemp := (vSign ? -0x8000000000000000 : 0) | (vPow << 52) | vNum
		VarSetCapacity(vData, 8)
		PuzPut(vTemp, &vData, "Int64")
		return PuzGet(&vData, "Double")
	}

	vIsSigned := !RegExMatch(vType, "i)^U")
	vSize := oType[RegExReplace(vType, "i)^U")]
	vNum := 0
	Loop vSize
		vNum |= oChar[Ord(StrGet(vAddr+A_Index-1, 1, "CP0"))] << (A_Index*8-8)
	if vIsSigned
		if (vSize = 1) && (vNum >= 128)
			return vNum - 256
		else if (vSize = 2) && (vNum >= 32768)
			return vNum - 65536
		else if (vSize = 4) && (vNum >= 2147483648)
			return vNum - 4294967296
	return vNum
}

PuzPut(vNum, vAddr, vType:="UPtr")
{
	local
	static oChar, oType := {Char:1, Short:2, Int:4, Int64:8, Ptr:A_PtrSize=8?8:4}
	static vIsReady := 0, vDataNull := 0
	if !vIsReady
	{
		;oChar := {0:""}
		oChar := {}
		VarSetCapacity(vDataNull, 1, 0)
		Loop 255
			oChar[A_Index] := Chr(A_Index)
		vIsReady := 1
	}

	if (vType = "Double")
	{
		vData := vNum + 0.0
		memcpy(vAddr, &vData, 8)
		return
	}

	if (vType = "Float")
	{
		vData := vNum + 0.0
		vNum := PuzGet(&vData, "Int64")

		;0x8000000000000000 (UInt64) = 0x8000000000000000 - 0x10000000000000000 (Int64) = -0x8000000000000000 (Int64)
		vSign := !!(vNum & -0x8000000000000000)
		vPow := ((vNum & 0x7FF0000000000000) >> 52)
		vNum := vNum & 0xFFFFFFFFFFFFF

		if (vPow = 2047)
			vPow := 255
		else if (vPow > 0)
			vPow := (vPow - 1023 + 127) & 0xFF
		;else if (vPow = 0)
		;	vPow := 0
		vBit := (vNum >> (52-24)) & 0x1 ;truncate number, but if bit is 1, round up
		vNum >>= (52-23)
;MsgBox vBit
;if (vBit)
;throw
		;handle rounding:
		;if 0
		if vBit
		{
			vTemp := 1
			Loop 22
			{
				if !(vNum & vTemp)
				{
					vNum |= vTemp
					vNum ^= vTemp-1
					break
				}
				vTemp <<= 1
			}
			if (vTemp = 2**22)
				vNum := 0, vPow += 1
		}

		vTemp := (vSign << 31) | (vPow << 23) | vNum
		PuzPut(vTemp, vAddr+0, "UInt")
		return
	}

	vSize := oType[RegExReplace(vType, "i)^U")]
	Loop Min(vSize, 7)
		vTemp := vNum & (0xFF << (A_Index*8-8))
		, vTemp >>= (A_Index*8-8)
		, (vTemp=0) ? memcpy(vAddr+A_Index-1, &vDataNull, 1) : memcpy(vAddr+A_Index-1, oChar.GetAddress(vTemp), 1)
	;0xFF00000000000000 (UInt64) = (0xFF00000000000000 - 0x10000000000000000) (Int64) = -0x100000000000000 (Int64)
	if (vSize = 8)
		vTemp := SubStr(Format("0x{:016X}", vNum & -0x100000000000000), 1, 4)
		, vTemp := Integer(vTemp)
		, (vTemp=0) ? memcpy(vAddr+7, &vDataNull, 1) : memcpy(vAddr+7, oChar.GetAddress(vTemp), 1)
}

memcpy(dest, src, count){
	/*
	void *memcpy(
		void *dest,
		const void *src,
		size_t count
	);
	url: https://msdn.microsoft.com/en-us/library/dswaw1wk.aspx (memcpy)
	*/
	return dllcall("MSVCRT.dll\memcpy", "ptr", dest, "ptr", src, "ptr", count, "cdecl")
}

;prepend Z to MsgBox, and it becomes a NoOp
ZMsgBox(oParams*)
{
}
To explain rounding in binary, here are 2 examples in decimal, where I want to remove the last digit, but since the last digit is 5 or higher, rounding up must be done:

0.999000999
0.99900100

0.999999999
1.00000000

In binary, if the last bit is 1, rounding up must be done. You start at the rightmost bit, and go left until you find a 0 bit, that 0 bit becomes a 1 bit, and the bits after it all become 0 bits.

0.111000111
0.11100100

0.111111111
1.00000000
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 3783
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

13 Apr 2019, 07:44

I don't see any explanation. Why are you rounding up?
User avatar
jeeswg
Posts: 6471
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Code Puzzle Thread

13 Apr 2019, 07:53

When you convert from a double to a float, decreasing precision, you have to round the number. Rounding down is simply truncation, rounding up, which I explained above, involves a bit of bit magic.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 3783
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

13 Apr 2019, 08:06

When you convert from a double to a float, decreasing precision, you have to round the number.
Why?
User avatar
jeeswg
Posts: 6471
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Code Puzzle Thread

13 Apr 2019, 08:30

- In my script, I take advantage of the fact that AutoHotkey natively uses doubles. I need to convert those doubles to floats. So the question is: how do you convert a double to a float.
- What you need to do is take the 3 numbers: sign, exponent, significand, and adjust them to decrease the precision, AFAIK this involves rounding. Initially I was only rounding down, and the script was getting around 50% of the conversions slightly mismatched. When I started handling rounding up, that figure was more like 1%.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 3783
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

13 Apr 2019, 08:44

So the question is: how do you convert a double to a float.
Initially I was only rounding down, and the script was getting around 50% of the conversions slightly mismatched. When I started handling rounding up, that figure was more like 1%.
So you should conclude that either, rounding up isn't the answer, or you are doing it wrong, or both.
User avatar
jeeswg
Posts: 6471
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Code Puzzle Thread

13 Apr 2019, 09:01

- The only thing I can conclude currently is that: the floats/doubles have not always been matching up exactly. I cannot even conclude that they *should* always be matching up exactly.
- If by training or by experimentation you have something to add on the matter, I would suggest you do so, either by clue, or just by giving a complete answer.
- I have limited time, and over the next few days I will be completing some mathematical tutorials, so any input would be better now rather than later.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Evil-e
Posts: 108
Joined: 04 Sep 2018, 11:09

Re: Code Puzzle Thread

20 Apr 2019, 05:01

Oh my god.... you pepes are soooooooooooo far over my head with all this code. After looking at some of the posts,
I am not even sure 1+1=2 anymore.... does it?
I have a bit of experience opening and sending commands to game console and CMD.exe... just ask :)
Helgef
Posts: 3783
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

28 Jun 2019, 08:15

Puzzle 12 - Not a regular bank heist

Puzzle description: You are a customer at a bank for wealthy people only, unfortunately, you made some bad business and are now broke. But there is some good news, you are skilled hacker and have managed to hack part of the bank's user interface and software. You now see your chance to get all the money, and get away with it!

Puzzle objectives:In this puzzle, you will study the bank software, then write a script which automates the user interface to transfer all the wealthy bank customer's money to your account and then clear the bank's internal logs to cover your tracks. When you run the bank software, you are logged in to your account. (Your name is Jane). When you have achieved the task, you account balance will be $12081334876 and you will be able to log out from the bank without alerting the police.

Puzzle rules: You can not modify the script below, you are only allowed to write a new script which automates the bank's user interface. No low level process memory or file manipulation is allowed. The script doesn't need to be completely automatic, but should be easy to use and come with clear instructions if needed.

The bank software:

Code: Select all

;
; You are not allowed to edit this code in any way:
;
; logged in details:
global logged_in_id := 16
global logged_in_name := "Jane"
; Transaction related:
global global_amount := 0			; The amount to transfer in the next transaction
global transfer_log := []			; Initialise transfer log

; Load account data:
global account_id_balance_map := [ 4840409, 6381577, 2136819, 3943104, 4877043, 44329046, 21340236, 10591744, 33157912, 5271471, 5459720666, 611417736, 153238799, 113762642, 5606323672, 2000]

global account_name_id_map :=	{ 	 Jimmy 		: 1,	Rosalynn 		: 2
									,Ronald		: 3, 	Nancy			: 4
									,George 	: 5,	Barbara			: 6
									,Bill 		: 7, 	Hillary			: 8,	Monica : 9
									,Georgew	: 10, 	Laura			: 11
									,Barack		: 12, 	Michelle		: 13
									,Donald 	: 14, 	Melania			: 15
									,(logged_in_name) : logged_in_id }



; UI:
global show_warning := true 		; default setting, show warning when incorrect password provided.
global bank_name := "Secure bank"	; The name of the bank
create_login_screen()				; Create the user interface
gui show,, % bank_name				; show the user interface

;
;	Bank software:
;

login(account_name, password) {
	static min_pw_len := 8
	if (strlen(password) < min_pw_len				; verify password is at least min_pw_len long.
		|| account_name ~= "i)\Q" . password . "\E" ; verify that the password isn't contained in the account_name to avoid calling verifyPassword with invalid password.
		|| !verifyPassword(account_name, password)) {
		if show_warning
			msgbox % 0x10,  % bank_name, % "Invalid password for account_name: " . account_name
		return 0
	}
	; Logged in successfully!
	
	logged_in_id := account_name_to_id(account_name)	; set the logged in account.
	logged_in_name := account_name
	
	; Update gui:
	gui_update_logged_in()
}

account_name_to_id(name_to_find) {
	for acc_name, id in account_name_id_map
		if (acc_name = name_to_find)
			return id
	return 0
}

transfer(source_account_name, dest_account_id) {
	; Verify input:
	if account_name_id_map.haskey(source_account_name)
		source_account_id := account_name_id_map[source_account_name]
	else {
		msgbox % 0x10,  % bank_name, % "Invalid source account: " source_account_name
		return false
	}
	if !account_id_balance_map.haskey(dest_account_id) || dest_account_id == source_account_id {
		msgbox % 0x10,  % bank_name, % "Invalid destination account: " dest_account_id
		return false
	}
	; All verified, do the transaction
	amount_to_transfer := global_amount == -1 ? account_id_balance_map[source_account_id] : global_amount 	; determine the amount to transfer.
	if (amount_to_transfer < 0) 
		return false
	
	if (amount_to_transfer > account_id_balance_map[source_account_id])
		return false
	
	account_id_balance_map[source_account_id] -= amount_to_transfer											; decrement source account balance.
	account_id_balance_map[dest_account_id] += amount_to_transfer											; increment destination account balance.
	; Log transfer
	log_handler("append", a_now .  "`n" . dest_account_id .  "`n" . source_account_id .  "`n" . amount_to_transfer)
	gui_refreshBalance(logged_in_id)	; Update the user interface to reflect the transaction.
	return true
}

log_handler(mode, what := 0) {
	; Handles the log of transactions
	; mode, string, one of:
	;	- append, append the log
	;	- verify_exit, verify the log is empty before logout
	;	- clear, clear items from the log, only done the relevant items has been verified.
	; what, string or integer, an item to log, or an integer defining the amount of logged items to clear, from the start of the log.
	if (mode == "append") {
		transfer_log.push(str)
	} else if (mode == "verify_exit") {
		; at this stage all transfers must have been verified elsewhere and removed from the transfer_log.
		if (transfer_log.length())
			return false
		; verified!
	} else if (mode == "clear") {
		if transfer_log.length() {
			if (what > transfer_log.length() || what < 1)
				throw exception("Invalid use")
			transfer_log.removeat(1, what)
		}
	}
	return true
}

;
;	User interface:
;

create_login_screen() {
	gui new
	gui add, tab3, , Login|Account|Transfer|Options|Log out
	gui add, text,, Account name:
	gui add, edit, w300
	gui add, text,, password:
	gui add, edit, w300 password
	gui add, button, w300 ggui_login, Login
	gui tab, Account
	gui add, text, w300, % "Account balance:`t$" . account_id_balance_map[logged_in_id]
	gui add, text, w300, Account name:`t%logged_in_name%
	gui add, text, w300, Account id:`t%logged_in_id%
	gui tab, Transfer
	gui add, text,, Amount:
	gui add, edit, w300, 0
	gui add, text,, To acount name:
	gui add, edit, w300
	gui add, checkbox, checked0, Transfer all.
	gui add, button, w300 ggui_transfer, Transfer
	gui tab, Options
	gui add, checkbox, checked1 gtoggle_warning, Show login warnings.
	gui tab, Log out
	gui add, button, w300 ggui_logout, Login out
}
gui_transfer() {
	
	guicontrolget amount_to_transfer,, Edit3
	guicontrolget to_account_name,, Edit4
	guicontrolget transfer_all,, Button2
	global_amount := transfer_all ? -1 : amount_to_transfer
	to_account_id := account_name_id_map[to_account_name]
	transfer(logged_in_name, to_account_id)
}
gui_update_logged_in() {
	guicontrol,, Static3, % "Account balance:`t$" . account_id_balance_map[logged_in_id]
	guicontrol,, Static4, Account name:`t%logged_in_name%
	guicontrol,, Static5, Account id:`t%logged_in_id%
}
gui_login() {
	guicontrolget account_name,, Edit1
	guicontrolget password,, Edit2
	login(account_name, password)
}
guiescape(){
	gui_logout()
}
guiclose(){
	gui_logout()
}

gui_logout() {
	if !log_handler("verify_exit")
		msgbox % "Suspicious transfers detected, police has been contacted."
	else
		msgbox % "Log out successful, welcome back soon."
	exitapp
}
gui_refreshBalance(id) {
	guicontrol,, Static3, % "Account balance:`t$" . account_id_balance_map[id]
}

toggle_warning(){
	show_warning := !show_warning
}

; Black box
verifyPassword(acc, pw){
	return false
}

New rule: for the first month, please only submit your solution to me via PM so that more people get the chance to try it out before the solution is made public. The first one to solve it gets two points, everyone else within the first month gets one point.

One month from now is the first of August.

Cheers and good luck.
User avatar
TheDewd
Posts: 1295
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Code Puzzle Thread

01 Jul 2019, 12:57

Puzzle 12 - Not a regular bank heist
Do passwords exist for the accounts? :crazy: Ha, this is hard to figure out! Quite challenging.
Image Bulldozer - Sokoban inspired game from 1994 recreated in AutoHotkey.
Helgef
Posts: 3783
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

01 Jul 2019, 13:26

puzzle 12 wrote:

Code: Select all

; Black box
verifyPassword(acc, pw){
	return false
}
Hehe, you can try any password you like, it will not be verified, but the puzzle can be solved ;)
Helgef
Posts: 3783
Joined: 17 Jul 2016, 01:02
Contact:

Re: Code Puzzle Thread

03 Jul 2019, 02:24

I'm happy to announce that we have a winner for Puzzle 12, CloakerSmoker has submitted an excellent solution to the puzzle and will be awarded two points. Everyone else are still welcome to submit their solutions to me via PM before the first of august, for that you will recieve one point and eternal glory. The solution will be made public on the first of august or short there after.

Cheers.

Return to “General Discussion”

Who is online

Users browsing this forum: No registered users and 0 guests