2024: Mosaic Retrospective

2024: Mosaic Retrospective

Not enough ratings
Mosaic Retrospective 2024 Cheating Tool Guide
By ax_pokl
This tool is designed to help players cheat in the game "Mosaic Retrospective 2024" by reading and modifying the game’s memory. It automatically solves the minefield by retrieving game process information, extracting map and grid data, modifying the grid status, and simulating clicks on cells to solve the game.
   
Award
Favorite
Favorited
Unfavorite
1. Obtaining the Game Process Handle
The tool first searches for the "Retrospective 2024" game window and retrieves the game’s process ID (PID).
Using OpenProcess, it opens the game’s process with full access, enabling it to read and write memory.
This provides control over the game’s internal data and logic.
2. Memory Search and Data Extraction
searchaddr_mn: Locating map data (numbers + region IDs)
This function scans the memory regions of type MEM_PRIVATE and PAGE_READWRITE.
It looks for a specific memory pattern that indicates the beginning of the tile data.

If a block matches:
  • A tile number (e.g., 60000)
  • Followed by $FFFFFFFF
  • And later $1A at offset +14 DWORDs
...then the following arrays are loaded:

Array
Description
mnw
The number on each tile (adjacent mine count)
mnid
The region ID each tile belongs to

Each mnid value corresponds to a connected zone or region in the game.

searchaddr_ms: Locating tile state overlay
This function searches another type of memory region (PAGE_NOACCESS) for a color pattern in the game's overlay layer.
Once found (e.g., 327682, $FF808080), it sets msoaddr and reads in the current overlay (mso), which represents visual tile states.
3. Initialization and Preprocessing
initmn: Preparing each tile’s mine logic
Initializes all internal status arrays (ms, msc, etc.).

For each tile, counts how many surrounding tiles (including itself) belong to the same mnid region → stored in mnr[j][/b].

If the number on the tile (mnw[j][/b]) is known, then:
mnb[i][j] := mnr[i][j] - mnw[i][j];
This defines how many mines must be placed among the neighboring tiles within the same region.
4. Looping Through the Grid to Solve and Write Back Status
checkmn: Core solving logic
Iterates through all unprocessed tiles (msc = 0), and applies logic:
  • If mnw[i][j] == msw[i][j], then all other neighbors must be mines → call solvemn(i,j,2)
  • If mnb[i][j] == msb[i][j], then all other neighbors must be safe → call solvemn(i,j,1)
solvemn: Applies mine or safe flag to neighbor tiles
For all surrounding tiles within the same mnid region:
If not yet marked, call setmn(x, y, s) to mark as:
  • 1 = safe
  • 2 = mine
setmn: Updates internal status of surrounding region
Sets ms[j] := s[/b], increments global counter mn, and updates:
  • msw[x][y] (safe neighbor count) if s = 1
  • msb[x][y] (mine neighbor count) if s = 2
Only updates tiles belonging to the same region ID.
5. Writing Updated State to the Game
writemn: Overlay update via memory write
For each tile:
  • If tile is safe (ms[i][j] = 1), write color
    $FF000000
  • If tile is a mine (ms[i][j] = 2), write color
    $FFFFFFFF
The tool writes the updated mso array back to the game’s memory using WriteProcessMemory, simulating visual interaction with the game.
Summary
This cheating tool enables automated solving of the Retrospective 2024 tile grid by leveraging internal region-based logic:
  1. Access the game’s process memory
  2. Identify and extract tile numbers and region IDs
  3. Initialize regional context and mine expectations
  4. Recursively evaluate tile logic to flag mines and safe spaces
  5. Write updated states back to the game display
Source Code
Program mosaic_retrospective_cheat; Uses Windows,display; const maxchr = 255; var phnd: thandle; var num: dword; var ret: boolean; const mw = 300; const mh = 200; var mn: longword; var mnw, mnb, mnid, mnr: array[0..mw-1, 0..mh-1] of cardinal; var msw, msb, ms, msc: array[0..mw-1, 0..mh-1] of shortint; var mso: array[0..mh-1, 0..mw-1] of longword; var msoaddr: longword; procedure initmn(); var i, j, x, y: longint; begin mn:=0; for i := 0 to mw-1 do for j := 0 to mh-1 do begin mnr[i,j]:= 0; for x:= i-1 to i+1 do for y:=j-1 to j+1 do if (x >= 0) and (x <= mw-1) and (y >= 0) and (y <= mh-1) then if (mnid[i,j]=mnid[x,y]) then mnr[i,j]:=mnr[i,j]+1; ms[i,j] := 0; msc[i,j] := 0; msb[i,j] := 0; msw[i,j] := 0; if mnw[i,j] = $FFFFFFFF then mnb[i,j] := $FFFFFFFF else mnb[i,j] := mnr[i,j] - mnw[i,j]; end; end; procedure setmn(i, j: longint; s: shortint); var x, y: longint; begin ms[i,j] := s; mn := mn+1; for x := i-1 to i+1 do for y := j-1 to j+1 do if (x >= 0) and (x <= mw-1) and (y >= 0) and (y <= mh-1) then if (mnid[i,j]=mnid[x,y]) then begin if (s = 1) then msw[x,y] := msw[x,y] + 1; if (s = 2) then msb[x,y] := msb[x,y] + 1; end; end; procedure solvemn(i, j: longint; s: shortint); var x, y: longint; begin msc[i,j] := s; for x := i-1 to i+1 do for y := j-1 to j+1 do if (x >= 0) and (x <= mw-1) and (y >= 0) and (y <= mh-1) then if (mnid[i,j]=mnid[x,y]) then if ms[x,y] = 0 then setmn(x, y, s); end; function checkmn(): longword; var i, j: longint; var nb, nw, n: longword; begin nb := 0; nw := 0; for i := 0 to mw - 1 do for j := 0 to mh - 1 do if msc[i, j] = 0 then begin if (mnw[i, j] = msw[i, j]) then begin solvemn(i, j, 2); nw := nw + 1; end else if (mnb[i, j] = msb[i, j]) then begin solvemn(i, j, 1); nb := nb + 1; end; end; n := nw + nb; checkmn := n; end; procedure writemn(); var i,j:longint; begin for i := 0 to mw-1 do for j := 0 to mh-1 do begin if mso[j,i]=$FF0000FF then continue else begin if ms[i,j]=1 then mso[j,i]:=$FF000000; if ms[i,j]=2 then mso[j,i]:=$FFFFFFFF; end; end; WriteProcessMemory(phnd, pointer(msoaddr), @mso, sizeof(mso), num); end; procedure getphnd; var pid: dword; win: hwnd; winname: pchar; begin getmem(winname, maxchr); repeat win := FindWindow(nil, 'Retrospective 2024'); GetWindowText(win, winname, maxchr); if pos('Retrospective 2024', winname) = 0 then win := 0; if win = 0 then begin win := GetForegroundWindow(); GetWindowText(win, winname, maxchr); if pos('Retrospective 2024', winname) = 0 then win := 0; end; if win = 0 then sleep(1); until win <> 0; GetWindowThreadProcessId(win, @pid); repeat phnd := OpenProcess(PROCESS_ALL_ACCESS, false, pid); if phnd = 0 then sleep(1); until phnd <> 0; end; procedure searchaddr_mn(base, lang: cardinal); var offset, addr, buff: cardinal; begin offset := $C; while (offset <= lang) do begin addr := base + offset; ret := ReadProcessMemory(phnd, pointer(addr), @buff, sizeof(buff), num); if ret and (buff = 60000) then begin addr := base + offset + 4; ReadProcessMemory(phnd, pointer(addr), @mnw, sizeof(mnw), num); addr := base + offset + 4 * 14; ret := ReadProcessMemory(phnd, pointer(addr), @buff, sizeof(buff), num); if ret and (buff = $0000001A) then ReadProcessMemory(phnd, pointer(base + offset + 4), @mnid, sizeof(mnid), num); end; offset := offset + $1000; end; end; procedure searchaddr_ms(base, lang: cardinal); const maxbuff = $10000; var offset, addr: cardinal; var buff: array[0..maxbuff] of cardinal; var k: longword; begin offset := 0; while (offset <= lang) do begin addr := base + offset; ret := ReadProcessMemory(phnd, pointer(addr), @buff, sizeof(buff), num); if ret then begin for k := 0 to maxbuff - 2 do if buff[k] = 327682 then if buff[k + 1] = $FF808080 then msoaddr := addr + (k+1) * 4; end; offset := offset + maxbuff * 4; end; ReadProcessMemory(phnd, pointer(msoaddr), @mso, sizeof(mso), num); end; procedure getaddr(); var MBI: MEMORY_BASIC_INFORMATION; currentmem: cardinal; nextmem: cardinal; nextmemp: ^cardinal; begin currentmem := 0; MBI.RegionSize := 0; repeat currentmem := currentmem + MBI.RegionSize; VirtualQueryEx(phnd, pointer(currentmem), MBI, sizeof(MBI)); nextmemp := @MBI.BaseAddress; nextmem := nextmemp^; with MBI do begin if (RegionSize >= $100000) and (AllocationProtect = PAGE_READWRITE) and (Protect = PAGE_READWRITE) and (State = MEM_COMMIT) and (_Type = MEM_PRIVATE) then searchaddr_mn(nextmem, MBI.RegionSize); if (RegionSize >= $100000) and (AllocationProtect = PAGE_NOACCESS) and (Protect = PAGE_READWRITE) and (State = MEM_COMMIT) and (_Type = MEM_PRIVATE) then searchaddr_ms(nextmem, MBI.RegionSize); end; until (currentmem <> nextmem) or (MBI.RegionSize = $FFFFFFFF); end; begin getphnd(); getaddr(); initmn(); while checkmn() > 0 do; writemn(); end.