Determine Beckett Grading Service (BGS) Final Grade

Charcoal, 73 71 bytes

≦⊗θ≔⌊θη≔⁻⌊Φθ⁻κ⌕θηηζI⊘⁺η∧ζ⎇⌕θη∨⎇⊖⌕θη∨›²ζ∨∧⁼²ζ‹¹⁶η∧⁼³⁻⌈θη›²⁰⌈θ›⁶ζ²⊕⌊⟦²÷ζ⁴

Try it online! Link is to verbose version of code. Takes input as an array of 4 values [Centring, Corners, Edges, Surface]. Explanation:

≦⊗θ

Double all the values so we're dealing in grade points rather than grades.

≔⌊θη

Find the worst grade.

≔⁻⌊Φθ⁻κ⌕θηηζ

Find the worst of the other grades, i.e. the second worst, and subtract the worst grade.

I⊘

Halve the final value and cast to string for display.

⁺η

Add the bonus to the minimum grade.

∧ζ

No bonus if the worst two grades are the same.

⎇⌕θη

Check whether the worst grade was for Centring.

∨⎇⊖⌕θη∨›²ζ∨∧⁼²ζ‹¹⁶η∧⁼³⁻⌈θη›²⁰⌈θ›⁶ζ²

If not then check whether only one bonus point should be awarded otherwise award two bonus points. (If the worst grade was for Corners then add a bonus point if the difference was less than 6 grade points or 2 if it was 6 or more. The calculation for Edges and Surface is longer but still only adds one or two bonus points.)

⊕⌊⟦²÷ζ⁴

If the worst grade was for Centring then add a bonus point for every 4 grade points between the worst two grades up to 2, plus a further bonus point.


Perl 5 (-ap), 157 154 bytes

minor improvements !$d?0:... inverted to $d?...:0, >=9.5 changed to >9.4 and !=10 to <10

($w,$x,$y,$z)=sort{$b<=>$a}@F;$d=$y-$z;$_=$z+=$d?$z==$F[2]|$z==$F[3]?$d<1?.5:$d==1&$z+1>9.4|$w-$z==1.5&$w<10?.5:1:$z==$F[0]?$d<2?.5:$d<4?1:1.5:$d<3?.5:1:0

TIO

first answer was

($w,$x,$y,$z)=sort{$b<=>$a}@F;$d=$y-$z;$_=$z+=!$d?0:$z==$F[2]|$z==$F[3]?$d<1?.5:$d==1&$z+1>=9.5|$w-$z==1.5&$w!=10?.5:1:$z==$F[0]?$d<2?.5:$d<4?1:1.5:$d<3?.5:1

straight forward, ungolfed

!$d?0
:$z==$F[2]|$z==$F[3]?
    $d<1?.5
    :$d==1&$z+1>=9.5|$w-$z==1.5&$w!=10?.5
    :1
:$z==$F[0]?
    $d<2?.5
    :$d<4?1
    :1.5
:$d<3?.5
:1

TIO


JavaScript (V8), 160 159 154 151 bytes

s=>{[C,O,E,S]=[...s];[a,b,c,d]=s.sort((a,b)=>b-a);e=c-d;return(e?d==E|d==S?e<1?.5:(e==1&d>8.4)|(a-d==1.5&a<10)?.5:1:d-C?e<3?.5:1:e<2?.5:e<4?1:1.5:0)+d}

Try it online!

A bit ungolfed:

function calculateScore(score) {
  [center,corner,edge,surface] = [...score]; // makes a copy because sort is in-place
  [first,second,third,fourth] = score.sort((a,b)=>b-a); // standard sort is alphabetically, this will sort by value
  diff = third - fourth;
  return fourth + (// Every final score is fourth + something else
    diff != 0
      ? fourth == edge || fourth == surface
        ? diff < 1
          ? 0.5
          : (diff == 1 && fourth >= 8.5) || (first-fourth == 1.5 && a < 10)
            ? 0.5
            : 1
      : fourth != corner
        ? diff < 3
          ? 0.5
          : 1
        : diff < 2
          ? 0.5
          : diff < 4
            ? 1
            : 1.5
      : 0 // diff == 0
  );
}
  • -1: >=8.5 -> >8.4
  • -5 thanks to @Kevin Cruijssen
  • -3 for ES6 copying with [...s]