-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7e31c5e
Showing
26 changed files
with
2,009 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
name = "cyberbot2077" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
winapi = { version = "0.3", features = ["winuser"] } | ||
clipboard-win = "4.4" | ||
bmp = "0.5" | ||
winput = "0.2" | ||
|
||
#tesseract = "0.12" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
# CyberBot 2077 | ||
|
||
Bot, who solves breach protocol games from Cyberpunk 2077. | ||
|
||
[![Preview](assets/preview.gif)](assets/preview.mp4) | ||
|
||
## Usage | ||
|
||
- [Download](#download) or [build](#build) the bot | ||
- Launch `cyberbot2077.exe` | ||
- Launch a breach protocol game | ||
- Move mouse cursor away. It must not obstruct the game field | ||
- Press `PrintScreen` keyboard button | ||
- Wait a second | ||
- ??? | ||
- PROFIT | ||
|
||
> Bot may consume `bmp` image path as last command line argument to print all possible solutions | ||
> ``` | ||
> cyberbot2077.exe path/to/image.bmp | ||
> ``` | ||
## How it works | ||
This bot uses [Tesseract](https://github.com/tesseract-ocr/tesseract) library as OCR engine. | ||
`PrintScreen` keyboard button triggers the bot, and it grabs screenshot | ||
to recognize game field (matrix, conditions, max step count and screen coordinates). | ||
![Recognize example](assets/recognize.jpg) | ||
``` | ||
Matrix: | ||
0xbd 0x55 0x55 0x7a 0xe9 0x7a 0x55 | ||
0x55 0xe9 0x55 0xbd 0x55 0x55 0xe9 | ||
0xe9 0x55 0xbd 0x7a 0x1c 0x55 0x7a | ||
0x55 0x1c 0x55 0x55 0x7a 0x1c 0xff | ||
0x1c 0x7a 0x7a 0x1c 0xbd 0x1c 0xbd | ||
0x1c 0x7a 0xe9 0xff 0x1c 0xe9 0xff | ||
0x1c 0x7a 0x7a 0xbd 0x7a 0x55 0xbd | ||
|
||
Conditions: | ||
0x1c 0x7a | ||
0x7a 0x1c 0x1c | ||
0x7a 0x7a 0xbd 0x7a | ||
|
||
Steps: 6 | ||
``` | ||
Then bot finds all solutions for each condition | ||
![All solution](assets/all.jpg) | ||
Tries to merge solutions together to cover as many conditions as possible: | ||
- One solution may be small piece of other | ||
- Ending of one solution may be beginning of other | ||
- 1-3 additional steps are required to merge them | ||
- ... | ||
Then all merged solutions must be finalized: | ||
first step is always vertical and contains any top item. | ||
Current example has 303 completed solutions. | ||
Bot filters the shortest solutions and applies solution, | ||
which covers as many conditions as possible (last conditions have greater score). | ||
![Best solutions](assets/best.jpg) | ||
``` | ||
Found 303 solutions | ||
6 best solutions: | ||
Solution #1, conditions: ✔ ✖ ✖, steps: 0xe9 0x1c 0x7a | ||
Solution #2, conditions: ✖ ✔ ✖, steps: 0x7a 0x1c 0x1c | ||
Solution #3, conditions: ✖ ✖ ✔, steps: 0x7a 0x7a 0xbd 0x7a | ||
Solution #4, conditions: ✔ ✔ ✖, steps: 0x7a 0x1c 0x1c 0x7a | ||
Solution #5, conditions: ✔ ✖ ✔, steps: 0x55 0x1c 0x7a 0x7a 0xbd 0x7a | ||
Solution #6, conditions: ✖ ✔ ✔, steps: 0x7a 0x7a 0xbd 0x7a 0x1c 0x1c | ||
``` | ||
Last solution `#6` will be applied. | ||
## Download | ||
Download bot [here](https://github.com/ricorodriges/cyberbot2077/releases). | ||
Bot uses `tesseract/tesseract.exe` binary. So please [download binaries](https://github.com/UB-Mannheim/tesseract/wiki) and extract to `tesseract` directory | ||
``` | ||
|- tesseract | ||
| |- tesseract.exe | ||
|- cyberbot2077.exe | ||
``` | ||
Then run `cyberbot2077.exe` and enjoy! | ||
## Build | ||
Since this project uses [Tesseract](https://github.com/tesseract-ocr/tesseract), | ||
you need to build it yourself or [download binaries](https://github.com/UB-Mannheim/tesseract/wiki). | ||
Bot uses `tesseract/tesseract.exe` binary. So please extract binaries to `tesseract` directory | ||
``` | ||
|- tesseract | ||
| |- tesseract.exe | ||
|- src | ||
|- test | ||
|- Cargo.toml | ||
``` | ||
Then you may run tests to make sure everything is ok and build | ||
```sh | ||
cargo test | ||
cargo build --release | ||
./target/release/cyberbot2077.exe | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
use std::path::Path; | ||
|
||
use bmp::{Image, Pixel, px}; | ||
use clipboard_win::{formats, get_clipboard}; | ||
|
||
pub fn load_img_from_clipboard() -> Option<Image> { | ||
return if let Ok(bytes) = get_clipboard(formats::Bitmap) { | ||
if bytes[0x1C] == 32 { | ||
// 32 bits per pixel :( | ||
// `bmp` crate does not support it. I don't want to include other crates. | ||
// convert it to 24 bits per pixel, ignore alpha channel | ||
let width = (bytes[0x12] as u32) | ((bytes[0x13] as u32) << 8); | ||
let height = (bytes[0x16] as u32) | ((bytes[0x17] as u32) << 8); | ||
|
||
let mut img = Image::new(width, height); | ||
for y in 0..height { | ||
for x in 0..width { | ||
let b = bytes[(0x36 + (x + y * width) * 4 + 0) as usize]; | ||
let g = bytes[(0x36 + (x + y * width) * 4 + 1) as usize]; | ||
let r = bytes[(0x36 + (x + y * width) * 4 + 2) as usize]; | ||
img.set_pixel(x, height - y - 1, px![r, g, b]); | ||
} | ||
} | ||
Some(img) | ||
} else { | ||
Some(bmp::from_reader(&mut &bytes[..]).unwrap()) | ||
} | ||
} else { | ||
None | ||
}; | ||
} | ||
|
||
pub fn load_img_from_file<P: AsRef<Path>>(path: P) -> Image { | ||
return bmp::open(path).unwrap(); | ||
} | ||
|
||
/// Clear image colors. Target `color` transforms to about black, other colors are white | ||
pub fn filter_img(img: &mut Image, color: &Pixel, range: u8) { | ||
for (x, y) in img.coordinates() { | ||
let src = img.get_pixel(x, y); | ||
let dest = Pixel::new(dif(src.r, color.r), dif(src.g, color.g), dif(src.b, color.b)); | ||
|
||
let result = if dest.r <= range && dest.g <= range && dest.b <= range { | ||
dest // about BLACK | ||
} else { | ||
bmp::consts::WHITE | ||
}; | ||
img.set_pixel(x, y, result); | ||
} | ||
} | ||
|
||
#[inline] | ||
fn dif(a: u8, b: u8) -> u8 { | ||
return (a as i16 - b as i16).abs() as u8; | ||
} | ||
|
||
pub fn crop_img(img: &Image, left: u32, top: u32, right: u32, bottom: u32) -> Image { | ||
let mut dest = Image::new(right - left, bottom - top); | ||
for (x, y) in dest.coordinates() { | ||
dest.set_pixel(x, y, img.get_pixel(x + left, y + top)); | ||
} | ||
return dest; | ||
} | ||
|
||
|
||
#[cfg(test)] | ||
mod tests { | ||
use bmp::{Pixel, px}; | ||
|
||
use crate::img::{crop_img, dif, filter_img}; | ||
|
||
#[test] | ||
fn test_dif() { | ||
assert_eq!(0, dif(0, 0)); | ||
assert_eq!(1, dif(0, 1)); | ||
assert_eq!(1, dif(1, 0)); | ||
assert_eq!(0, dif(255, 255)); | ||
assert_eq!(1, dif(255, 254)); | ||
assert_eq!(1, dif(254, 255)); | ||
assert_eq!(255, dif(0, 255)); | ||
assert_eq!(255, dif(255, 0)); | ||
} | ||
|
||
#[test] | ||
fn test_filter_img() { | ||
let mut img = bmp::Image::new(4, 4); | ||
img.set_pixel(0, 0, px!(0, 10, 20)); | ||
img.set_pixel(1, 0, px!(10, 20, 30)); | ||
img.set_pixel(0, 1, px!(15, 25, 35)); | ||
img.set_pixel(1, 1, px!(20, 30, 40)); | ||
|
||
filter_img(&mut img, &px!(10, 20, 30), 5); | ||
|
||
assert_eq!(px!(255, 255, 255), img.get_pixel(0, 0)); | ||
assert_eq!(px!(0, 0, 0), img.get_pixel(1, 0)); | ||
assert_eq!(px!(5, 5, 5), img.get_pixel(0, 1)); | ||
assert_eq!(px!(255, 255, 255), img.get_pixel(1, 1)); | ||
|
||
assert_eq!(4, img.get_width()); | ||
assert_eq!(4, img.get_height()); | ||
} | ||
|
||
#[test] | ||
fn test_crop_img() { | ||
let mut img = bmp::Image::new(4, 4); | ||
img.set_pixel(0, 0, px!(0, 0, 0)); | ||
img.set_pixel(1, 0, px!(10, 20, 30)); | ||
img.set_pixel(0, 1, px!(0, 0, 0)); | ||
img.set_pixel(1, 1, px!(0, 0, 0)); | ||
|
||
let crop = crop_img(&img, 1, 0, 2, 1); | ||
|
||
assert_eq!(1, crop.get_width()); | ||
assert_eq!(1, crop.get_height()); | ||
assert_eq!(px!(10, 20, 30), crop.get_pixel(0, 0)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
use std::thread::sleep; | ||
use std::time::Duration; | ||
use winput::{Action, Button, Input, Mouse}; | ||
|
||
pub fn click(dx: i32, dy: i32) { | ||
Mouse::move_relative(dx, dy); | ||
sleep(Duration::from_millis(300)); | ||
|
||
let input = Input::from_button(Button::Left, Action::Press); | ||
winput::send_inputs(&[input]); | ||
|
||
sleep(Duration::from_millis(30)); | ||
|
||
let input = Input::from_button(Button::Left, Action::Release); | ||
winput::send_inputs(&[input]); | ||
sleep(Duration::from_millis(200)); | ||
} |
Oops, something went wrong.