Skip to content

Commit

Permalink
add observe support
Browse files Browse the repository at this point in the history
  • Loading branch information
Tnze committed May 30, 2023
1 parent bef774a commit b5c8920
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 60 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@
- 自动排序,将更好的宏排在靠前位置,无需手动记录最优解
- 离线使用,秒开秒用,体验不受网络环境影响
- 程序极小,电脑硬盘空间不足的玩家也可以放心安装
- 跨平台,(支持Linux与MacOS系统)Windows用户请在[发行版页面](https://gitee.com/Tnze/ffxiv-best-craft/releases)下载`.msi`安装包
- 跨平台,(支持Linux与MacOS系统)

缺点:

- 开启“掌握”支持时求解器占用内存较大
- 用的人太少了(?)

## Download 下载

Windows用户请在[发行版页面](https://gitee.com/Tnze/ffxiv-best-craft/releases)下载包含`.msi`字样的安装包

Linux 与 MacOS 用户请移步[Github Releases](https://github.com/Tnze/ffxiv-best-craft/releases)下载对应的安装包

- Mac 用户请下载`dmg``app`文件
- Debian 及 Ubuntu 用户请下载`AppImage``deb`文件
- 其他 Linux 用户请下载`AppImage`文件

## Dev (开发人员帮助)

### 安装依赖项
Expand Down
137 changes: 86 additions & 51 deletions src-tauri/src/dynamic_programing_solver.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ffxiv_crafting::{Actions, Status, Buffs};
use ffxiv_crafting::{Actions, Buffs, Status};
use micro_ndarray::Array;
use std::cell::Cell;

Expand All @@ -9,7 +9,7 @@ struct SolverSlot<T> {
action: Option<Actions>,
}

const SYNTH_SKILLS: [Actions; 9] = [
const SYNTH_SKILLS: [Actions; 11] = [
Actions::BasicSynthesis,
Actions::WasteNot,
Actions::Veneration,
Expand All @@ -19,9 +19,11 @@ const SYNTH_SKILLS: [Actions; 9] = [
Actions::DelicateSynthesis,
Actions::IntensiveSynthesis,
Actions::PrudentSynthesis,
Actions::Observe,
Actions::FocusedSynthesis,
];

const TOUCH_SKILLS: [Actions; 13] = [
const TOUCH_SKILLS: [Actions; 15] = [
Actions::BasicTouch,
Actions::MastersMend,
Actions::WasteNot,
Expand All @@ -35,31 +37,46 @@ const TOUCH_SKILLS: [Actions; 13] = [
Actions::AdvancedTouch,
Actions::TrainedFinesse,
Actions::Manipulation,
Actions::Observe,
Actions::FocusedTouch,
];

pub struct QualitySolver {
progress_solver: ProgressSolver,
mn: usize,
mn: bool,
wn: usize,
// results [iq][iv][gs][mn][wn][touch][d][cp]
results: Array<Cell<Option<SolverSlot<u32>>>, 8>,
obz: bool,
// results [obz][iq][iv][gs][mn][wn][touch][d][cp]
results: Array<Cell<Option<SolverSlot<u32>>>, 9>,
}

impl QualitySolver {
pub fn new(init_status: Status, mn: usize, wn: usize) -> Self {
pub fn new(init_status: Status, mn: bool, wn: usize, obz: bool) -> Self {
let cp = init_status.attributes.craft_points as usize;
let du = init_status.recipe.durability as usize;
let progress_solver = ProgressSolver::new(init_status, mn, wn);
let progress_solver = ProgressSolver::new(init_status, mn, wn, obz);
Self {
progress_solver,
wn,
mn,
results: Array::new([11, 5, 4, mn + 1, wn + 1, 3, du / 5 + 1, cp + 1]),
obz,
results: Array::new([
obz as usize + 1,
11,
5,
4,
mn as usize * 8 + 1,
wn + 1,
3,
du / 5 + 1,
cp + 1,
]),
}
}

fn get(&self, s: &Status) -> &Cell<Option<SolverSlot<u32>>> {
let i = [
s.buffs.observed as usize,
s.buffs.inner_quiet as usize,
s.buffs.innovation as usize,
s.buffs.great_strides as usize,
Expand Down Expand Up @@ -96,28 +113,22 @@ impl QualitySolver {
step: 0,
action: None,
};
for sk in &TOUCH_SKILLS {
match sk {
&Actions::Manipulation if self.mn == 0 => continue,
&Actions::WasteNot | &Actions::WasteNotII if self.wn == 0 => continue,
_ => {}
for sk in TOUCH_SKILLS {
if (matches!(sk, Actions::Manipulation) && !self.mn)
|| (matches!(sk, Actions::WasteNotII) && self.wn < 8)
|| (matches!(sk, Actions::WasteNot) && self.wn < 4)
|| (matches!(sk, Actions::Observe) && !self.obz)
|| (matches!(sk, Actions::FocusedTouch) && s.buffs.observed == 0)
{
continue;
}
if s.is_action_allowed(*sk).is_err() {
if s.is_action_allowed(sk).is_err() {
continue;
}

let mut new_s = s.clone();
new_s.buffs = Buffs {
great_strides: s.buffs.great_strides,
innovation: s.buffs.innovation,
inner_quiet: s.buffs.inner_quiet,
manipulation: s.buffs.manipulation,
wast_not: s.buffs.wast_not,
touch_combo_stage: s.buffs.touch_combo_stage,
..Buffs::default()
};
new_s.quality = 0;
new_s.cast_action(*sk);
new_s.cast_action(sk);

let progress = self.progress_solver.inner_read(&new_s).value;
if new_s.progress + progress >= new_s.recipe.difficulty {
Expand All @@ -132,7 +143,7 @@ impl QualitySolver {
best = SolverSlot {
value: quality,
step,
action: Some(*sk),
action: Some(sk),
}
}
}
Expand All @@ -147,10 +158,20 @@ impl crate::solver::Solver for QualitySolver {

fn read(&self, s: &Status) -> Option<Actions> {
if s.is_finished() {
return None
return None;
}
let max_quality = s.recipe.quality;
let mut new_s = s.clone();
new_s.buffs = Buffs {
great_strides: s.buffs.great_strides,
innovation: s.buffs.innovation,
inner_quiet: s.buffs.inner_quiet,
manipulation: s.buffs.manipulation,
wast_not: s.buffs.wast_not,
touch_combo_stage: s.buffs.touch_combo_stage,
observed: s.buffs.observed,
..Buffs::default()
};
let max_addon = max_quality - s.quality;
let mut best = {
let SolverSlot {
Expand Down Expand Up @@ -183,25 +204,36 @@ impl crate::solver::Solver for QualitySolver {
/// ProgressSolver 是一种专注于推动进展的求解器,给定玩家属性和配方并经过初始化后,
/// 对于任意的当前状态,可以以O(1)时间复杂度算出剩余资源最多可推多少进展。
pub struct ProgressSolver {
mn: usize,
mn: bool,
wn: usize,
// results[ve][mm][mn][wn][d][cp]
results: Array<Cell<Option<SolverSlot<u16>>>, 6>,
obz: bool,
// [obz][ve][mm][mn][wn][d][cp]
results: Array<Cell<Option<SolverSlot<u16>>>, 7>,
}

impl ProgressSolver {
pub fn new(init_status: Status, mn: usize, wn: usize) -> Self {
pub fn new(init_status: Status, mn: bool, wn: usize, obz: bool) -> Self {
let cp = init_status.attributes.craft_points as usize;
let du = init_status.recipe.durability as usize;
Self {
mn,
wn,
results: Array::new([5, 6, mn + 1, mn + 1, du / 5 + 1, cp + 1]),
obz,
results: Array::new([
obz as usize + 1,
5,
6,
mn as usize * 8 + 1,
wn + 1,
du / 5 + 1,
cp + 1,
]),
}
}

fn get(&self, s: &Status) -> &Cell<Option<SolverSlot<u16>>> {
let i = [
s.buffs.observed as usize,
s.buffs.veneration as usize,
s.buffs.muscle_memory as usize,
s.buffs.manipulation as usize,
Expand Down Expand Up @@ -236,26 +268,21 @@ impl ProgressSolver {
step: 0,
action: None,
};
for sk in &SYNTH_SKILLS {
match *sk {
Actions::Manipulation if self.mn < 9 => continue,
Actions::WasteNot if self.wn < 5 => continue,
Actions::WasteNotII if self.wn < 9 => continue,
_ => {}
for sk in SYNTH_SKILLS {
if (matches!(sk, Actions::Manipulation) && !self.mn)
|| (matches!(sk, Actions::WasteNotII) && self.wn < 8)
|| (matches!(sk, Actions::WasteNot) && self.wn < 4)
|| (matches!(sk, Actions::Observe) && !self.obz)
|| (matches!(sk, Actions::FocusedSynthesis) && s.buffs.observed == 0)
{
continue;
}
if s.is_action_allowed(*sk).is_err() {
if s.is_action_allowed(sk).is_err() {
continue;
}
let mut new_s = s.clone();
new_s.buffs = Buffs{
muscle_memory: s.buffs.muscle_memory,
veneration: s.buffs.veneration,
manipulation: s.buffs.manipulation,
wast_not: s.buffs.wast_not,
..Buffs::default()
};
new_s.progress = 0;
new_s.cast_action(*sk);
new_s.cast_action(sk);
let mut progress = new_s.progress;
let mut step = 1;
if new_s.durability > 0 {
Expand All @@ -267,7 +294,7 @@ impl ProgressSolver {
best = SolverSlot {
value: progress,
step,
action: Some(*sk),
action: Some(sk),
}
}
}
Expand All @@ -281,7 +308,7 @@ impl crate::solver::Solver for ProgressSolver {

fn read(&self, s: &Status) -> Option<Actions> {
if s.is_finished() {
return None
return None;
}
let difficulty = s.recipe.difficulty;
let max_addon = difficulty - s.progress;
Expand All @@ -295,6 +322,14 @@ impl crate::solver::Solver for ProgressSolver {
(progress, step, action)
};
let mut new_s2 = s.clone();
new_s2.buffs = Buffs {
muscle_memory: s.buffs.muscle_memory,
veneration: s.buffs.veneration,
manipulation: s.buffs.manipulation,
wast_not: s.buffs.wast_not,
observed: s.buffs.observed,
..Buffs::default()
};
for cp in 0..=s.craft_points {
new_s2.craft_points = cp;
for du in 1..=s.durability {
Expand Down Expand Up @@ -343,7 +378,7 @@ mod test {
#[test]
fn test() {
let init_status = init();
let solver = ProgressSolver::new(init_status.clone(), 8, 8);
let solver = ProgressSolver::new(init_status.clone(), true, 8, true);
let actions = solver.read_all(&init_status);
println!("{actions:?}");
}
Expand All @@ -352,7 +387,7 @@ mod test {
fn test2() {
let mut init_status = init();
init_status.cast_action(ffxiv_crafting::Actions::Reflect);
let solver = QualitySolver::new(init_status.clone(), 8, 8);
let solver = QualitySolver::new(init_status.clone(), true, 8, true);
let actions = solver.read_all(&init_status);
println!("{actions:?}");
}
Expand Down
8 changes: 5 additions & 3 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ async fn create_solver(
status: Status,
use_muscle_memory: bool,
use_manipulation: bool,
use_obzerve: bool,
app_state: tauri::State<'_, AppState>,
) -> Result<(), String> {
let key = solver::SolverHash {
Expand All @@ -340,14 +341,15 @@ async fn create_solver(
Box::new(preprogress_solver::PreprogressSolver::new(
status,
progress_list,
use_manipulation as usize * 8,
use_manipulation,
8,
))
} else {
Box::new(dynamic_programing_solver::QualitySolver::new(
status,
use_manipulation as usize * 8,
8,
use_manipulation,
8 + 1,
use_obzerve,
))
};
*solver_slot.lock().await = Some(solver);
Expand Down
6 changes: 3 additions & 3 deletions src-tauri/src/preprogress_solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub struct PreprogressSolver {
}

impl PreprogressSolver {
pub fn new(init_status: Status, progress_list: Vec<u16>, mn: usize, wn: usize) -> Self {
pub fn new(init_status: Status, progress_list: Vec<u16>, mn: bool, wn: usize) -> Self {
let progress_index = progress_list
.iter()
.scan(0, |prev, &x| {
Expand All @@ -30,7 +30,7 @@ impl PreprogressSolver {
.map(|v| {
let mut s = init_status.clone();
s.progress = s.recipe.difficulty - *v;
QualitySolver::new(s, mn, wn)
QualitySolver::new(s, mn, wn, false)
})
.collect();
Self {
Expand Down Expand Up @@ -107,7 +107,7 @@ mod test {
#[test]
fn test() {
let mut status1 = init();
let solver = PreprogressSolver::new(status1.clone(), preprogress_list(&status1), 8, 8);
let solver = PreprogressSolver::new(status1.clone(), preprogress_list(&status1), true, 8);
let init_actions = [
Actions::MuscleMemory,
Actions::Manipulation,
Expand Down
4 changes: 3 additions & 1 deletion src/Solver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { invoke } from "@tauri-apps/api/tauri";
export const create_solver = (
status: Status,
useMuscleMemory: boolean,
useManipulation: boolean
useManipulation: boolean,
useObzerve: boolean,
) => {
return invoke("create_solver", {
status,
useMuscleMemory,
useManipulation,
useObzerve,
});
};

Expand Down
Loading

0 comments on commit b5c8920

Please sign in to comment.