Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on: pull_request

jobs:
test:
runs-on: ubuntu-latest
runs-on: windows-latest

steps:
- name: Checkout code
Expand All @@ -13,21 +13,13 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12' # Use the appropriate version of Python
python-version: '3.12'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Install Xvfb
run: sudo apt-get install -y xvfb

- name: Run tests
run: |
sudo Xvfb :99 -ac -screen 0 1920x1080x24 &
export DISPLAY=:99
touch /tmp/.Xauthority
export XAUTHORITY=/tmp/.Xauthority
python -m unittest discover -s test -p "_test_*.py"

4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,7 @@ dmypy.json

# JetBrains
.idea

# Project
/test/*/settings/
/settings/*.json
67 changes: 66 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,66 @@
# AutoHackerPDA
# AutoHackerPDA

AutoHackerPDA is a tool designed to automate the process of hacking PDAs in the game Thief Simulator. It simplifies the hacking mini-game, allowing you to focus on the main aspects of the game.

## Features

- Automatically hacks PDAs in Thief Simulator
- Easy to use interface
- Configurable settings for different hacking scenarios

## Requirements

- Thief Simulator from Steam

## Installation

1. Download the latest version of AutoHackerPDA from the [Releases](https://github.com/yourusername/AutoHackerPDA/releases) page.
2. Extract the downloaded zip file to a location of your choice.
3. Run `AutoHackerPDA.exe` to start the application.

## Usage

1. Launch Thief Simulator and navigate to the PDA hacking mini-game.
2. Run `AutoHackerPDA.exe`.
3. Configure the settings as per your requirement.
4. Click the "Start Hacking" button.
5. The tool will automatically complete the hacking mini-game for you.

## Configuration

AutoHackerPDA comes with several configuration options to customize the hacking process. You can adjust these settings in the application interface:

- **Speed**: Adjust the speed of the hacking process.
- **Retries**: Set the number of retries in case of a failed attempt.
- **Logging**: Enable or disable logging for debugging purposes.

## Contributing

We welcome contributions to improve AutoHackerPDA. If you would like to contribute, please follow these steps:

1. Fork the repository.
2. Create a new branch for your feature or bugfix.
3. Make your changes and commit them with a clear message.
4. Submit a pull request to the main repository.

Please read our [Contributing Guidelines](CONTRIBUTING.md) for more details.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Support

If you encounter any issues or have questions about AutoHackerPDA, please open an issue in the [issue tracker](https://github.com/yourusername/AutoHackerPDA/issues) or contact us at support@yourproject.com.

## Disclaimer

AutoHackerPDA is intended for use in Thief Simulator only. Use of this tool in any other context may violate the terms of service of the software or platform.

## About me

If you want to know more about me or my other projects, visit my [GitHub profile](https://github.com/LukasKrah)

---

Thank you for using AutoHackerPDA! We hope you enjoy the enhanced gameplay experience.
2 changes: 1 addition & 1 deletion algorithm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@
Created: 19/04/2023
"""

from ._algorithm_service import AlgorithmService
from ._algorithm import Algorithm
60 changes: 30 additions & 30 deletions screen_service/_output_service.py → algorithm/_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,45 @@

Contact me: l.krahbichler@proton.me

File: screen_service/_output_service.py
Created: 18/04/2023
File: algorithm/_algorithm.py
Created: 04/08/2024
"""

##################################################
# Imports #
##################################################

from time import sleep
import pyautogui
import autoit
from cv2.typing import MatLike

from ._simulation import Simulation


##################################################
# Code #
##################################################

class OutputService:
"""
Do something one the screen
"""

@classmethod
def pad_clicks(cls, click: dict[tuple[int, int], int]) -> None:
width: int
height: int
width, height = pyautogui.size()

x_left: int = (width // 3) + 40
x_right: int = int(width / 1.536) - 30
y_top: int = int(height / 3.85714)
y_bot: int = int(height / 1.30120)

field_width = (x_right - x_left) // 8
field_height = (y_bot - y_top) // 8

for row, col in click:
x: int = x_left + (field_width * col) + (field_width // 2)
y: int = y_top + (field_height * row) + (field_height // 2)
for i in range(click[(row, col)]):
autoit.mouse_click("left", x, y, speed=0)
sleep(0.02)
class _Algorithm:
__sim: Simulation

def __init__(self) -> None:
self.__sim = Simulation()

def path_finder(
self,
_pads: list[list[MatLike]],
pad_directions: list[list[str]],
io_ports: tuple[int, int]
) -> dict[tuple[int, int], int]:
self.__sim.update_pads(pad_directions)
self.__sim.update_io_ports(io_ports)

his = self.__sim.simulate()

diffs = self.__sim.found_diffs(his)
# row | column

# Window.grid_pads(_pads, self.__sim.get_directions(), io_ports)
return diffs


Algorithm = _Algorithm()
141 changes: 141 additions & 0 deletions algorithm/_circuit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"""
AutoHackerPDA: A program that automates the process of "Hacking PDA" in the game "Thief Simulator".
Copyright (C) 2024 Lukas Krahbichler

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

Contact me: l.krahbichler@proton.me

File: algorithm/_circuit.py
Created: 04/08/2024
"""

##################################################
# Imports #
##################################################

from __future__ import annotations

from typing import Literal

from ._types import CIRCUIT, CIRCUIT_TYPE, DIRECTIONS, DIRECTIONS_TYPE


##################################################
# Code #
##################################################

# Todo: Performance measurements and improvements
# Todo: Tests
class Circuit:
_type: CIRCUIT_TYPE | None
_direction: DIRECTIONS_TYPE | None
_is_powered: bool

_top_port_power: bool
_bot_port_power: bool
_right_port_power: bool
_left_port_power: bool

def __init__(self):
self._type = None
self._direction = None
self._is_powered = False

self._top_port_power = False
self._bot_port_power = False
self._right_port_power = False
self._left_port_power = False

def __update(self) -> None:
# Facing: Top -> top, left, bot, right
emitting: tuple[bool, bool, bool, bool] = (False,) * 4

if self._type == "oneway":
emitting = (True, False, True, False)
elif self._type == "corner":
emitting = (True, False, False, True)
elif self._type == "junction":
emitting = (True, True, False, True)

if self._direction:
self._top_port_power = emitting[(0 - DIRECTIONS.index(self._direction)) % 4]
self._left_port_power = emitting[(1 - DIRECTIONS.index(self._direction)) % 4]
self._bot_port_power = emitting[(2 - DIRECTIONS.index(self._direction)) % 4]
self._right_port_power = emitting[(3 - DIRECTIONS.index(self._direction)) % 4]

def __repr__(self) -> str:
return f"Model: {self._type} - Facing: {self._direction} - Ports(T, L, B, R): " \
f"{self._top_port_power}, {self._left_port_power}, {self._bot_port_power}, {self._right_port_power}"

def set_attributes(self, pad_string: str) -> None:
cut_string: list[Literal["oneway", "corner", "junction", "", "top", "bot", "left", "right"]]
cut_string = pad_string.split("_") # noqa

if len(cut_string) >= 2:
self._type = cut_string[0] if cut_string[0] in CIRCUIT else ""
self._direction = cut_string[1] if cut_string[1] in DIRECTIONS else ""
else:
self._type = None
self._direction = None
self.__update()

def rotate_by(
self,
by: int | None = 1
) -> None:
if self._direction:
self._direction = DIRECTIONS[(DIRECTIONS.index(self._direction) + by) % 4]
self.__update()

@property
def state(self) -> bool:
return self._is_powered

@state.setter
def state(self, value: bool) -> None:
self._is_powered = value

def empower(self, direction: DIRECTIONS_TYPE | None) -> None:
self._is_powered = self.__dict__[f"_Pad__port_{direction}"]
self.__update()

@property
def port_top(self) -> bool:
return self._top_port_power

@property
def port_left(self) -> bool:
return self._left_port_power

@property
def port_bot(self) -> bool:
return self._bot_port_power

@property
def port_right(self) -> bool:
return self._right_port_power

@property
def facing(self) -> DIRECTIONS_TYPE | None:
return self._direction

@facing.setter
def facing(self, value: DIRECTIONS_TYPE | None) -> None:
self._direction = value
self.__update()

@property
def model(self) -> CIRCUIT_TYPE | None:
return self._type
Loading