PQ1 gambling exploit

Caught in your own cuffs? Need a hint? Or just want to talk about Police Quest - this is the place to do it!
Post Reply
User avatar
notbobsmith
Village Elder
Posts: 5376
Joined: Sun Mar 09, 2014 4:02 pm
Location: Massachusetts
Gender: Male

PQ1 gambling exploit

Post by notbobsmith »

I found this on Youtube:



The short version is that the amount of money in your wallet is stored in an 8-bit unsigned integer (0-255). To make it appear that you have more money, the game just appends a "0" at the end. So the variable stores a 68, but displays $680. And this is where the exploit comes in. If you bring the money you have to only $10, when you go to zero while gambling, your money will roll over back to 255 (or $2550) since the variable can't go negative. The game thinks you made a killing in the game and you automatically advance. Makes sense.

Except...

I remembered that if you buy a drink at the bar, they cost $5. Which means that sometimes the money in your wallet can end in a 0 or a 5. So I tried this out, and if you enter the poker game with just $5 in your wallet, your money will suddenly become $2555 after you pay the $10 ante. So what appears to be happening is that there is a separate flag which determines if a 0 or a 5 gets displayed at the end. And if you look at your wallet, when you have just $5 left, it will actually display $05. 0 with a 5 at the end. What I can't figure out is, why do it this way? Wouldn't it be simpler to use a 16-bit integer?
User avatar
Tawmis
Grand Poobah's Servant
Posts: 20935
Joined: Wed Oct 08, 2008 1:19 am
Gender: Not Specified
Contact:

Re: PQ1 gambling exploit

Post by Tawmis »

Interesting. Curious how no one in QA caught that. ;)
User avatar
Rath Darkblade
The Cute One
Posts: 12944
Joined: Fri Oct 24, 2008 5:15 am
Location: Lost in Translation
Gender: Male
Contact:

Re: PQ1 gambling exploit

Post by Rath Darkblade »

*looks up "difference between 8-bit unsigned integer and 16-bit integer" on google* ;)

All right. "8 bit" refers to any number in binary from 0 to 255, in binary written as 0000 0000 to 1111 1111 (those are 8 “bits” or binary digits).

16 bit refers to numbers from 0 to 65535.

So, maybe they simply didn't want Sonny to be able to bet so much? :) $65,535 is a lot of money, even in fictional games.

What would make sense is dividing the number into three bytes, where:

- Byte #1 being only 0 to 2; and
- Byte #2 and #3 being 0 to 9 -- UNLESS ...
- If Byte #1 is "2", then Byte #2 can only be 0 to 5, and Byte #3 can be 0 to 9 -- UNLESS ...
- If Byte #1 is "2", and Byte #2 is "5", then Byte #3 can only be 0 to 5. :D

This is a bit fiddly, but logic-wise, it can be done. Behold:

Code: Select all

Let_Byte1 = 0, 1, or 2;
Let_Byte2 = 0 to 9;
Let_Byte3 = 0 to 9;

IF Byte1 = 2
AND Byte2 = 5
THEN Let_Byte3 = 0 to 5
ELSE Byte2 = 0 to 9
AND Byte3 = 0 to 9
END IF
Just translate that to C++, or whichever language you like, and you're ready to rock 'n roll. :twisted:
User avatar
notbobsmith
Village Elder
Posts: 5376
Joined: Sun Mar 09, 2014 4:02 pm
Location: Massachusetts
Gender: Male

Re: PQ1 gambling exploit

Post by notbobsmith »

Rath Darkblade wrote: Sat May 11, 2024 11:07 pm *looks up "difference between 8-bit unsigned integer and 16-bit integer" on google* ;)

All right. "8 bit" refers to any number in binary from 0 to 255, in binary written as 0000 0000 to 1111 1111 (those are 8 “bits” or binary digits).

16 bit refers to numbers from 0 to 65535.

So, maybe they simply didn't want Sonny to be able to bet so much? :) $65,535 is a lot of money, even in fictional games.

What would make sense is dividing the number into three bytes, where:

- Byte #1 being only 0 to 2; and
- Byte #2 and #3 being 0 to 9 -- UNLESS ...
- If Byte #1 is "2", then Byte #2 can only be 0 to 5, and Byte #3 can be 0 to 9 -- UNLESS ...
- If Byte #1 is "2", and Byte #2 is "5", then Byte #3 can only be 0 to 5. :D

This is a bit fiddly, but logic-wise, it can be done. Behold:

Code: Select all

Let_Byte1 = 0, 1, or 2;
Let_Byte2 = 0 to 9;
Let_Byte3 = 0 to 9;

IF Byte1 = 2
AND Byte2 = 5
THEN Let_Byte3 = 0 to 5
ELSE Byte2 = 0 to 9
AND Byte3 = 0 to 9
END IF
Just translate that to C++, or whichever language you like, and you're ready to rock 'n roll. :twisted:
The high dollar value is only a problem if the player uses the exploit, which they don't seem concerned about. On the other hand, if a regular integer was used, you could have the player lose if they go negative.

I don't think I follow what your code is trying to do. A simpler solution, which I think it what they did, is to have the unsigned integer hold the money, and another variable to hold if you have a five or zero. so if you buy a drink from the bartender, it would look something like this:

Code: Select all

if (five == true) {
     five = false; //subtract 5 if you have a 5
}
else {
     money = money - 1;  // subtract "10"
     five = true;  // add "5"
}
But again, using a 16-bit integer just seems simpler.
User avatar
Rath Darkblade
The Cute One
Posts: 12944
Joined: Fri Oct 24, 2008 5:15 am
Location: Lost in Translation
Gender: Male
Contact:

Re: PQ1 gambling exploit

Post by Rath Darkblade »

notbobsmith wrote: Sun May 12, 2024 12:12 am I don't think I follow what your code is trying to do.
I'm not sure what the problem is. My code is simply saying as follows:

1. Sonny can only have up to $255.
2. Therefore, we divide the number "255" into three variables, where:

a. Byte1 represents the "hundreds" digit (and can be 0, 1 or 2);
b. Byte2 represents the "tens" digit (and can be 0 to 9);
c. Byte3 represents the "singles" digit (and can be 0 to 9).

3. BUT, to avoid instances of Sonny getting $299 (which won't work! :)), we add two caveats:

a. If "Byte1" is "2" (i.e. Sonny has $200 and above), then "Byte2" can only be 0 to 5 (i.e. up to $250 and up).
b. If "Byte1" is "2" AND "Byte2" is 5 (i.e. $250-something), then "Byte 3" can only be 0 to 5 (to avoid Sonny getting $256, $257 etc.)
c. Otherwise (i.e. Sonny has between $0 and $200) ... then "Byte 2" and "Byte 3" can be 0 to 9. (And then Sonny can have $0 to $99, or $100 to $199, etc.) :)
notbobsmith wrote: Sun May 12, 2024 12:12 am But again, using a 16-bit integer just seems simpler.
Agreed. I'm not sure why Sierra didn't do that. Then again, according to wikipedia:
A 16-bit register can store 2-to-the-power-of-16 different values. The range of integer values that can be stored in 16 bits depends on the integer representation used. With the two most common representations, the range is 0 through 65,535 for representation as an (unsigned) binary number.
So if you're using a 16-bit integer, Sonny can have up to $65,535. That's incredibly generous for a poker game. :D
User avatar
notbobsmith
Village Elder
Posts: 5376
Joined: Sun Mar 09, 2014 4:02 pm
Location: Massachusetts
Gender: Male

Re: PQ1 gambling exploit

Post by notbobsmith »

Rath Darkblade wrote: Sun May 12, 2024 7:57 pm
notbobsmith wrote: Sun May 12, 2024 12:12 am I don't think I follow what your code is trying to do.
I'm not sure what the problem is. My code is simply saying as follows:

1. Sonny can only have up to $255.
2. Therefore, we divide the number "255" into three variables, where:

a. Byte1 represents the "hundreds" digit (and can be 0, 1 or 2);
b. Byte2 represents the "tens" digit (and can be 0 to 9);
c. Byte3 represents the "singles" digit (and can be 0 to 9).

3. BUT, to avoid instances of Sonny getting $299 (which won't work! :)), we add two caveats:

a. If "Byte1" is "2" (i.e. Sonny has $200 and above), then "Byte2" can only be 0 to 5 (i.e. up to $250 and up).
b. If "Byte1" is "2" AND "Byte2" is 5 (i.e. $250-something), then "Byte 3" can only be 0 to 5 (to avoid Sonny getting $256, $257 etc.)
c. Otherwise (i.e. Sonny has between $0 and $200) ... then "Byte 2" and "Byte 3" can be 0 to 9. (And then Sonny can have $0 to $99, or $100 to $199, etc.) :)
notbobsmith wrote: Sun May 12, 2024 12:12 am But again, using a 16-bit integer just seems simpler.
Agreed. I'm not sure why Sierra didn't do that. Then again, according to wikipedia:
A 16-bit register can store 2-to-the-power-of-16 different values. The range of integer values that can be stored in 16 bits depends on the integer representation used. With the two most common representations, the range is 0 through 65,535 for representation as an (unsigned) binary number.
So if you're using a 16-bit integer, Sonny can have up to $65,535. That's incredibly generous for a poker game. :D
That seems really complicated when devising a way to add and subtract.

"1. Sonny can only have up to $255." Why? Remember that the only reason that there is the $2550 limit is because they used the unsigned 8-bit integer. It doesn't have to have that limit. In practice, this is just fine. Sonny starts with $1000, spends $320 before entering the game and has to do well playing poker. The poker session ends when he wins enough. It's unlikely that he will get past $2000. I usually end with ~$1500. You only encounter the "problem" if you deliberately try the exploit.
User avatar
Rath Darkblade
The Cute One
Posts: 12944
Joined: Fri Oct 24, 2008 5:15 am
Location: Lost in Translation
Gender: Male
Contact:

Re: PQ1 gambling exploit

Post by Rath Darkblade »

OK, fair enough. I thought that having an 8-bit integer means that 1 to 256, as wikipedia told me when I looked up "8-bit integers". :)

As for it being complicated - teaching a computer to do math (from scratch) is complicated. I was thinking of a way to do it when all you have is 3 bytes. ;)
Post Reply

Return to “The Police Quest Series”