Help to make a speeding ticket program

Hi i found an old topic from 2016, speeding ticket program, do anyone has the code for that?
Here is how is should look like when finished:

It was quite interesting so i if anyone has the solution i would be grateful

Overspeed by 17 results in a 115 euro fine. Overspeed by 5 results in 70 euro fine.

Assuming that the fine is base+overage*rate, we have two formulas:

base + 17 * rate = 115
base + 5 * rate = 70

Subtracting the bottom from the top gives us:

base - base + (17 - 5) * rate = 115 - 70
12 * rate = 45

It’s not possible to use that base + n * rate to gain an integer solution, so something else must be going on there.

I see from https://speedingeurope.com/ that 21+ is required for a speeding fine, so it doesn’t seem to be based on the real world either.

It looks like further details is needed about how the infraction fine is to be calculated.

The original topic (for a school assignment) has more details:

Thanks. Using that previous information, I’ve come up with the following simple set of assertions.

console.assert(speedLimit(50, 60).type === "", "Under limit doesn't get fined");
console.assert(speedLimit(60, 60).type === "", "On the limit doesn't get fined");
console.assert(speedLimit(61, 60).type === "Infraction", "Infraction when over by up to 15");
console.assert(speedLimit(61, 60).fine === 85, "Fined 85 for up to 15 over");
console.assert(speedLimit(75, 60).type === "Infraction", "Infraction when over by up to 15");
console.assert(speedLimit(75, 60).fine === 85, "Fined 85 for up to 15 over");
console.assert(speedLimit(76, 60).type === "Infraction", "Infraction when over by up to 20");
console.assert(speedLimit(76, 60).fine === 115, "Fined 115 for up to 20 over");
console.assert(speedLimit(80, 60).type === "Infraction", "Infraction when over by up to 20");
console.assert(speedLimit(76, 60).fine === 115, "Fined 115 for up to 20 over");
console.assert(speedLimit(81, 60).type === "Unit", "Unit fine when over by more than 20");
console.assert(speedLimit(121, 120).type === "Infraction", "Infraction when over by up to 15");
console.assert(speedLimit(121, 120).fine === 70, "Fined 70 for up to 20 over");
console.assert(speedLimit(135, 120).type === "Infraction", "Infraction when over by up to 15");
console.assert(speedLimit(135, 120).fine === 70, "Fined 70 for up to 20 over");
console.assert(speedLimit(136, 120).type === "Infraction", "Infraction when over by up to 15");
console.assert(speedLimit(136, 120).fine === 100, "Fined 100 for up to 20 over");
console.assert(speedLimit(140, 120).type === "Infraction", "Infraction when over by up to 15");
console.assert(speedLimit(140, 120).fine === 100, "Fined 100 for up to 20 over");
console.assert(speedLimit(141, 120).type === "Unit", "Unit fine when over by more than 20");

That results in the following basic code that passes all of those assertions:

function checkSpeed(speed, limit) {
    const speedInfo = {
        type: "",
        fine: 0
    };
    const infractionFines = {
        60: {
            15: 85,
            20: 115
        },
        120: {
            15: 70,
            20: 100
        }
    };
    const excessSpeed = speed - limit;
    if (excessSpeed <= 0) {
        return speedInfo;
    }
    if (excessSpeed > 20) {
        speedInfo.type = "Unit";
        return speedInfo;
    }
    if (excessSpeed > 0) {
        speedInfo.type = "Infraction";
    }
    if (excessSpeed <= 20) {
        speedInfo.fine = infractionFines[limit][20];
    }
    if (excessSpeed <= 15) {
        speedInfo.fine = infractionFines[limit][15];
    }
    return speedInfo;
}

The rest of the code to make it work with a form can be found at https://codepen.io/pmw57/pen/GRWEKYB too.

2 Likes

I feel like there’s… mathematics shortcutting that can be done on the logic here…

the category of the zone is given by limit // 65; (Result: 0 or 1.)
the category of the speeding is given by (speed - limit) // 15; (Result: 0 or 1; any values > 1 should be caught by the outer check)
the fine is equal to 70+(30*speedingcat)+(15*!zonecat)

(You’d need an overall “if speeding > 20, go to unit” check, but)

EDIT, and a Tangent: I spent an annoying amount of time trying to figure out if there was a non-logical way of doing Not on a 0/1 input. Because my mind went a-wandering into maths land.
(x-1)*(x-1)
(if x = 1, then 0*0 = 0)
(if x = 0, then -1 * -1 = 1)

There are multiple reasons that I haven’t taken this further. One is that this is supposed to be someone’s homework, and another is that i would be procrastinating against other things that should get done.

Him, if I procrastinate on this until tomorrow then I won’t need to procrastinate any longer and can delve deeper into the situation here, Yeah, that should work :slight_smile:

3 Likes

I agree… though by that same token, my theory is if I reduce a coding challenge down to a single mathematical formula, and someone submits it to their teacher for coding homework, the teacher’s immediately going to google where they got their answer from…

1 Like

I’m forking off new codePen project from https://codepen.io/pmw57/pen/GRWEKYB to https://codepen.io/pmw57/pen/zYZzPqZ so that the original one doesn’t change.

While the original code does the job, there are several improvements that can be made. These types of improvements are called refactoring, because the changes to the code make no changes to the behaviour of the code, and instead make it easier for us to understand and make improvements to the code.

The checkSpeed function does two different things. One is to set the infraction type, and the other is to set the fine. We can extract that code out to two separate functions.

Use a getFineType function

The getFineType function only needs to know the excess speed. If it’s less than 1 then there’s no fine type. If it’s less than 21 then it’s an Infraction, and anything else beyond that is a Unit fine.

function getFineType(excessSpeed) {
    if (excessSpeed < 1) {
        return "";
    }
    if (excessSpeed < 21) {
        return "Infraction";
    }
    return "Unit";
}

We can now replace the old code that sets speedinfo.type, with one call to the getFineType function, and exit the checkSpeed function when it’s not an infraction.

    const excessSpeed = speed - limit;
    speedInfo.type = getFineType(excessSpeed);
    if (speedInfo.type !== "Infraction") {
        return speedInfo;
    }

Refactoring getFineType

The series of values for a type of thing can be simplified. I’m thinking of using an object to store fine types against a set of values.

    const fineTypes = {
        1: "",
        21: "Infraction",
        99999: "Unit"
    };
}

If you manage to clock something going faster than 99999, you’ve most likely caught some space object which is beyond your jurisdiction.

I can start the conversion slowly with just the object and assigning a found fineType.

function getFineType(excessSpeed) {
    const fineTypes = {
        1: "",
        21: "Infraction",
        99999: "Unit"
    };
    let [limit, fineType] = Object.entries(fineTypes).find(
        function ([limit, fineType]) {
            return excessSpeed < limit;
        }
    );
    ...
}

I can then remove the fineType assignment one by one from each if statement, checking that all of the asserts still pass.

    if (excessSpeed < 1) {
        console.log(fineType);
        // fineType = "";
        return fineType;
    }
    if (excessSpeed < 21) {
        // fineType = "Infraction";
        return fineType;
    }
    if (excessSpeed < 99999) {
        // fineType = "Unit";
        return fineType;
    }

Then remove all of the if statements, and just return the fineType. That let statement can be replaced with a const one instead too.

function getFineType(excessSpeed) {
    const fineTypes = {
        1: "",
        21: "Infraction",
        99999: "Unit"
    };
    const [limit, fineType] = Object.entries(fineTypes).find(
        function ([limit, fineType]) {
            return excessSpeed < limit;
        }
    );
    return fineType;
}

Extract the fineTypes object

The fineTypes object can now be extracted out of the function, and passed in as configuration information instead.

function getFineType(excessSpeed, fineTypes) {
    const [limit, fineType] = Object.entries(fineTypes).find(
        function ([limit, fineType]) {
            return excessSpeed < limit;
        }
    );
    return fineType;
}
function checkSpeed(speed, limit) {
    ...
    const fineTypes = {
        1: "",
        21: "Infraction",
        99999: "Unit"
    };
    const excessSpeed = speed - limit;
    speedInfo.type = getFineType(excessSpeed, fineTypes);
    ...
}

Remove excessive indenting

The getFineTypes function now has too much indenting. We can see a distinctive arrow-formation to the indents. We can extract that find function out to a separate one called minSpeedExcess.

function getFineType(excessSpeed, fineTypes) {
    function minSpeedExcess([limit, fineType]) {
        return excessSpeed < limit;
    }
    const [limit, fineType] = Object.entries(fineTypes).find(minSpeedExcess);
    return fineType;
}

I want to extract that minSpeedExcess function fully outside of the getFineType function, but currently it’s trapped in there due to the excessSpeed that it checks. That can be remedied by using a wrapper function for the find function, so that the excessSpeed can be kept there by the wrapper.

function minSpeedExcess(limit, fineType, excessSpeed) {
    return excessSpeed < limit;
}
function minSpeedExcessWrapper(excessSpeed) {
    return function ([limit, fineType]) {
        return minSpeedExcess(limit, fineType, excessSpeed);
    };
}
function getFineType(excessSpeed, fineTypes) {
    const findMinSpeedExcess = minSpeedExcessWrapper(excessSpeed);
    const [limit, fineType] = Object.entries(fineTypes).find(findMinSpeedExcess);
    return fineType;
}

Applying some finishing touches

There are a couple of improvements that can be made to the minSpeedExcess and wrapper function. The fineType isn’t needed now, so that can be removed.

// function minSpeedExcess(limit, fineType, excessSpeed) {
function minSpeedExcess(limit, excessSpeed) {
    return excessSpeed < limit;
}
function minSpeedExcessWrapper(excessSpeed) {
    return function ([limit, fineType]) {
        // return minSpeedExcess(limit, fineType, excessSpeed);
        return minSpeedExcess(limit, excessSpeed);
    };
}

And another improvement is that we aren’t really checking for a min speed excess. Instead we are checking if the excess is beyond a certain limit, so let’s rename the function to that.

// function minSpeedExcess(limit, excessSpeed) {
function excessWithinLimit(limit, excessSpeed) {
    return excessSpeed < limit;
}
// function minSpeedExcessWrapper(excessSpeed) {
function excessWithinLimitWrapper(excessSpeed) {
    // return function ([limit, fineType]) {
    return function findExcessLimit([limit, fineType]) {
        // return minSpeedExcess(limit, excessSpeed);
        return excessWithinLimit(limit, excessSpeed);
    };
}
...
    // const findMinSpeedExcess = minSpeedExcessWrapper(excessSpeed);
    const findExcessLimit = excessWithinLimitWrapper(excessSpeed);
    // const [limit, fineType] = Object.entries(fineTypes).find(findMinSpeedExcess);
    const [limit, fineType] = Object.entries(fineTypes).find(findExcessLimit);

We have now extracted that getFineType function out into the following functions:

function excessWithinLimit(limit, excessSpeed) {
    return excessSpeed < limit;
}
function excessWithinLimitWrapper(excessSpeed) {
    return function findExcessLimit([limit, fineType]) {
        return excessWithinLimit(limit, excessSpeed);
    };
}
function getFineType(excessSpeed, fineTypes) {
    const findExcessLimit = excessWithinLimitWrapper(excessSpeed);
    const [limit, fineType] = Object.entries(fineTypes).find(findExcessLimit);
    return fineType;
}

Finally, because the excessWithinLimit function has parameters of limit and excessSpeed, it can be less confusing if the function uses them in that same order too.

function excessWithinLimit(limit, excessSpeed) {
    return limit > excessSpeed;
}

The end result of that refactoring can be seen at https://codepen.io/pmw57/pen/zYZzPqZ

Because I’ve had the asserts running every time I change something in the code, simple mistakes I made were trivial to find, and more complicated issues have been easy to diagnose and resolve.

Next up beyond this is to create a getFineAmount function, but that’s for a different post.

Now that the infraction type is being handled by a function, we can move on to doing something similar with the infraction fine amount.

Allow limit to be returned along with infraction type

We will want to know the type of limit that was breached, so we can update getFineType to be getFineInfo and instead return an object that contains both the type, and the limit that was breached.

function getFineInfo(excessSpeed, fineTypes) {
    const findExcessLimit = excessWithinLimitWrapper(excessSpeed);
    const [limit, fineType] = Object.entries(fineTypes).find(findExcessLimit);
    return {
        limit: Number(limit),
        type: fineType
    };
}
...
function checkSpeed(speed, limit) {
    ...
    const fineInfo = getFineInfo(excessSpeed, fineTypes);
    // speedInfo.type = getFineType(excessSpeed, fineTypes);
    speedInfo.type = fineInfo.type;
    ...
}

Use a consistent limit breach value

We now have access to fineInfo.limit which tells us which limit was breached.

Instead of using 1 and 21 as limit breach amounts, I’m wanting 0, 15 and 20 to be that limit. We can adjust the fine limits so that the 15 and 20 is given back to us, and slightly adjust the excessWithinLimit function condition to keep things working.

function excessWithinLimit(limit, excessSpeed) {
    // return limit > excessSpeed;
    return limit >= excessSpeed;
}
...
    const fineTypes = {
        0: "",
        15: "Infraction",
        20: "Infraction",
        99999: "Unit"
    };

Doing things that way, gives us meaningful limit information about whether it was the 15 or the 20 limit that was breached.

Use the limit breach value

We can now replace the if statement conditions with ones using fineInfo.limit instead:

    // if (excessSpeed <= 20) {
    if (fineInfo.limit === 20) {
        speedInfo.fine = infractionFines[limit][20];
    }
    // if (excessSpeed <= 15) {
    if (fineInfo.limit === 15) {
        speedInfo.fine = infractionFines[limit][15];
    }

We can now remove those if statements completely and replace them with one single simple line that does the job:

        speedInfo.fine = infractionFines[limit][fineInfo.limit];

Use a more specific variable name than limit

I should rename the limit variable to be more specific, that it refers to the speedLimit instead.

// function checkSpeed(speed, limit) {
function checkSpeed(speed, speedLimit) {
    ...
    // const excessSpeed = speed - limit;
    const excessSpeed = speed - speedLimit;
    ...
    // speedInfo.fine = infractionFines[limit][fineInfo.limit];
    speedInfo.fine = infractionFines[speedLimit][fineInfo.limit];
    ...
}

Simplify the infraction decisions

The following code can now be adjusted:

    if (speedInfo.type !== "Infraction") {
        return speedInfo;
    }
    speedInfo.fine = infractionFines[speedLimit][fineInfo.limit];
    return speedInfo;

so that only if the fine type is Infraction that it updates the fine.

    if (speedInfo.type === "Infraction") {
        speedInfo.fine = infractionFines[speedLimit][fineInfo.limit];
    }
    return speedInfo;

I can even remove the two fineInfo and speedInfo objects, and use only the one fineInfo object instead.

function checkSpeed(speed, speedLimit) {
    const excessSpeed = speed - speedLimit;
    const fineInfo = getFineInfo(excessSpeed, fineTypes);
    if (fineInfo.type === "Infraction") {
        fineInfo.fine = infractionFines[speedLimit][fineInfo.limit];
    }
    return fineInfo;
}

Use a fineConfig object

I think lastly I want to move the objects for fineTypes and infractionFines out to a fineConfig object.

const fineConfig = {
    types: {
        0: "",
        15: "Infraction",
        20: "Infraction",
        99999: "Unit"
    },
    infractions: {
        60: {
            15: 85,
            20: 115
        },
        120: {
            15: 70,
            20: 100
        }
    }
};
...
    // const fineInfo = getFineInfo(excessSpeed, fineTypes);
    const fineInfo = getFineInfo(excessSpeed, fineConfig.types);
...
        // speedInfo.fine = infractionFines[speedLimit][fineInfo.limit];
        speedInfo.fine = fineConfig.infractions[speedLimit][fineInfo.limit];

Summary

I don’t think that we’ll need a separate function for dealing with the fine part afterall, now that we’ve used refactoring to help simplify things.

Here is the resulting checkSpeed function:

function checkSpeed(speed, speedLimit) {
    const excessSpeed = speed - speedLimit;
    const fineInfo = getFineInfo(excessSpeed, fineConfig.types);
    if (fineInfo.type === "Infraction") {
        fineInfo.fine = fineConfig.infractions[speedLimit][fineInfo.limit];
    }
    return fineInfo;
}

The final code we end up with is at https://codepen.io/pmw57/pen/PopjRGV

There might be, but I think that the following infraction config information may be a more reliable way to do things.

    infractions: {
        60: {
            15: 85,
            20: 115
        },
        120: {
            15: 70,
            20: 100
        }
    }

About the only improvement I might make from there is to make the speed limit, the excess speed and the fine amount spelled out more clearly in the object.

I mean, perhaps. I just don’t see a need to walk objects in this simple system?

function getFine(excess,limit) {
if (excess > 20) { return {"limit": limit, "type": "unit", "fine" : undefined } } //Undefined because we're not given the unit fines.
zonecat = floor(limit / 65);
zonecat = (zonecat-1)*(zonecat-1);
return {"limit": limit, "type": "Infraction", "fine" : 70+(30*floor(excess / 15))+(15*zonecat) };
}

gets all of the information in the above?

If the system needs to be extendable, or often modified, then certainly.

1 Like

There are many ways for the homework to be done.
In my case it gave a handy excuse to exercise some refactoring skills too. :eyebrows:

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.