double evaluateFingerObstacleCost(FingerState state, float minPreparation, NoteChord obsNote, float startPosition, int startHeight, int pitch) { float deltaX = startPosition - Piano.KeyPositions[pitch]; float deltaY = startHeight - Piano.getKeyHeight(pitch); double distance = Math.Min(Math.Sqrt(deltaX * deltaX + deltaY * deltaY), 5) + 1; float prepareTime = startTime - minPreparation; float importance = NotationUtils.getNoteImportanceInChord(obsNote, state.Pitch); debug += string.Format("O{0},{1},{2},{3};", state.Pitch, prepareTime, state.Release, state.Press); if (prepareTime >= state.Release) { double coeff = CostCoeff.FINGER_MOVE_SPEED_PUNISH * Math.Pow(0.1, (startTime - state.Release) / minPreparation); return(coeff * distance); } else if (prepareTime >= state.Press) { double speedCost = CostCoeff.FINGER_MOVE_SPEED_PUNISH * distance; double cutoffCost = CostCoeff.NOTE_CUTOFF_PUNISH * importance * (state.Release - prepareTime) / (state.Release - state.Press); return(speedCost + cutoffCost); } else { return(CostCoeff.OMIT_KEY_PUNISH * importance); } }
Choice evaluateChordChoice(FingerChord chord, int index) { float deltaTime = index > 0 ? NoteSeq[index].start - NoteSeq[index - 1].start : 0; NoteChord note = NoteSeq[index]; HandConfig.RangePair wrists = Config.getFingerChordWristRange(chord); double cost = 0; // wrist position naturality reward if (wrists.left != null) { float distance = Math.Abs(wrists.left.middle - -HandConfig.WristNaturePosition); cost = Math.Pow(distance / 14, 4) * CostCoeff.WRIST_POSITION_NATURALITY_REWARD; } if (wrists.right != null) { float distance = Math.Abs(wrists.right.middle - HandConfig.WristNaturePosition); cost = Math.Pow(distance / 14, 4) * CostCoeff.WRIST_POSITION_NATURALITY_REWARD; } // wrist crowd punish if (wrists.left != null && wrists.right != null) { float distance = Math.Max(Math.Abs(wrists.left.high - wrists.right.low), Math.Abs(wrists.right.high - wrists.left.low)); if (distance < 5) { cost += CostCoeff.WRIST_CROWD_PUNISH * (5f - distance) / 5f; } } foreach (Finger f in chord.Values) { // shift fingers punish if (Math.Abs((int)f) > 10) { cost += CostCoeff.SHIFT_FINGERS_PUNISH; } } int leftFingerCount = 0; int rightFingerCount = 0; foreach (var pair in chord) { if (pair.Value != Finger.EMPTY) { // black key short punish if (Piano.isBlackKey(pair.Key)) { int finger = Math.Abs((int)pair.Value); int first = (int)Math.Floor(finger / 10f) - 1; int second = finger % 10 - 1; float sh = HandConfig.BlackKeyShort[second]; if (first >= 0) { sh = Math.Max(HandConfig.BlackKeyShort[first], sh); } cost += sh * CostCoeff.BLACK_KEY_SHORT_PUNISH; } if (pair.Value > Finger.EMPTY) { ++rightFingerCount; } else if (pair.Value < Finger.EMPTY) { ++leftFingerCount; } } else { // omit key punish float importance = NotationUtils.getNoteImportanceInChord(note, pair.Key); cost += CostCoeff.OMIT_KEY_PUNISH * importance; } } // multiple fingers punish if (leftFingerCount > 0) { float value = leftFingerCount / 5f; cost += CostCoeff.MULTIPLE_FINGERS_PUNISH * value * value; } if (rightFingerCount > 0) { float value = rightFingerCount / 5f; cost += CostCoeff.MULTIPLE_FINGERS_PUNISH * value * value; } return(new Choice { chord = chord, staticCost = cost, wrists = wrists, deltaTime = deltaTime, node = new TreeNodeChoice() }); }