## Charcoal, 73 71 bytes

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


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 of the other grades, i.e. the second worst, and subtract the worst grade.

Ｉ⊘


Halve the final value and cast to string for display.

⁺η


∧ζ


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]