Beispiel #1
0
def builtin_date(name, args, pos):
    """
    date(year, month, day) builtin function.

    @return Days since 1 jan 1 of the given date.
    """
    days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    if len(args) != 3:
        raise generic.ScriptError("date() requires exactly 3 arguments", pos)
    identifier.ignore_all_invalid_ids = True
    year = args[0].reduce(global_constants.const_list)
    identifier.ignore_all_invalid_ids = False
    try:
        month = args[1].reduce_constant().value
        day = args[2].reduce_constant().value
    except generic.ConstError:
        raise generic.ScriptError(
            "Month and day parameters of date() should be compile-time constants",
            pos)
    generic.check_range(month, 1, 12, "month", args[1].pos)
    generic.check_range(day, 1, days_in_month[month - 1], "day", args[2].pos)

    if not isinstance(year, ConstantNumeric):
        if month != 1 or day != 1:
            raise generic.ScriptError(
                "when the year parameter of date() is not a compile time constant month and day should be 1",
                pos)
        # num_days = year*365 + year/4 - year/100 + year/400
        part1 = nmlop.MUL(year, 365)
        part2 = nmlop.DIV(year, 4)
        part3 = nmlop.DIV(year, 100)
        part4 = nmlop.DIV(year, 400)
        res = nmlop.ADD(part1, part2)
        res = nmlop.SUB(res, part3)
        res = nmlop.ADD(res, part4)
        return res

    generic.check_range(year.value, 0, 5000000, "year", year.pos)
    day_in_year = 0
    for i in range(month - 1):
        day_in_year += days_in_month[i]
    day_in_year += day
    if month >= 3 and (year.value % 4 == 0) and ((not year.value % 100 == 0) or
                                                 (year.value % 400 == 0)):
        day_in_year += 1
    return ConstantNumeric(
        year.value * 365 + calendar.leapdays(0, year.value) + day_in_year - 1,
        pos)
Beispiel #2
0
def cargo_profit_value(value):
    # In NFO, calculation is (amount * price_factor * cb_result) / 8192
    # Units of the NML price factor differ by about 41.12, i.e. 1 NML unit = 41 NFO units
    # That'd make the formula (amount * price_factor * cb_result) / (8192 / 41)
    # This is almost (error 0.01%) equivalent to the following, which is what this calculation does
    # (amount * price_factor * (cb_result * 329 / 256)) / 256
    # This allows us to report a factor of 256 in the documentation, which makes a lot more sense than 199.804...
    # Not doing the division here would improve accuracy, but limits the range of the return value too much
    value = nmlop.MUL(value, 329)
    return nmlop.DIV(value, 256)
Beispiel #3
0
def parse_property_value(prop_info, value, unit=None, size_bit=None):
    """
    Parse a single property value / unit
    To determine the value that is to be used in nfo

    @param prop_info: A dictionary with property information
    @type prop_info: C{dict}

    @param value: Value of the property
    @type value: L{Expression}

    @param unit: Unit of the property value (e.g. km/h)
    @type unit: L{Unit} or C{None}

    @param size_bit: Bit that indicates the size of a multitile house
                     Set iff the item is a house
    @type size_bit: C{int} or C{None}

    @return: List of values to actually use (in nfo) for the property
    @rtype: L{Expression}
    """
    # Change value to use, except when the 'nfo' unit is used
    if unit is None or unit.type != "nfo":
        # Save the original value to test conversion against it
        org_value = value

        # Multiply by property-specific conversion factor
        mul, div = 1, 1
        if "unit_conversion" in prop_info:
            mul = prop_info["unit_conversion"]
            if isinstance(mul, tuple):
                mul, div = mul

        # Divide by conversion factor specified by unit
        if unit is not None:
            if "unit_type" not in prop_info or unit.type != prop_info[
                    "unit_type"]:
                raise generic.ScriptError("Invalid unit for property",
                                          value.pos)
            unit_mul, unit_div = unit.convert, 1
            if isinstance(unit_mul, tuple):
                unit_mul, unit_div = unit_mul
            mul *= unit_div
            div *= unit_mul

        # Factor out common factors
        gcd = generic.greatest_common_divisor(mul, div)
        mul //= gcd
        div //= gcd

        if isinstance(value,
                      (expression.ConstantNumeric, expression.ConstantFloat)):
            # Even if mul == div == 1, we have to round floats and adjust value
            value = expression.ConstantNumeric(
                int(float(value.value) * mul / div + 0.5), value.pos)
            if unit is not None and "adjust_value" in prop_info:
                value = adjust_value(value, org_value, unit,
                                     prop_info["adjust_value"])
        elif mul != div:
            # Compute (value * mul + div/2) / div
            value = nmlop.MUL(value, mul)
            value = nmlop.ADD(value, int(div / 2))
            value = nmlop.DIV(value, div)

    elif isinstance(value, expression.ConstantFloat):
        # Round floats to ints
        value = expression.ConstantNumeric(int(value.value + 0.5), value.pos)

    # Apply value_function if it exists
    if "value_function" in prop_info:
        value = prop_info["value_function"](value)

    # Make multitile houses work
    if size_bit is not None:
        num_ids = house_sizes[size_bit]
        assert "multitile_function" in prop_info
        ret = prop_info["multitile_function"](value, num_ids, size_bit)
        assert len(ret) == num_ids
        return ret
    else:
        return [value]
Beispiel #4
0
def transform_bin_op(assignment):
    op = assignment.value.op
    expr1 = assignment.value.expr1
    expr2 = assignment.value.expr2
    extra_actions = []

    if op == nmlop.CMP_GE:
        expr1, expr2 = expr2, expr1
        op = nmlop.CMP_LE

    if op == nmlop.CMP_LE:
        extra_actions.extend(parse_actionD(ParameterAssignment(assignment.param, nmlop.SUB(expr1, expr2))))
        op = nmlop.CMP_LT
        expr1 = assignment.param
        expr2 = expression.ConstantNumeric(1)

    if op == nmlop.CMP_GT:
        expr1, expr2 = expr2, expr1
        op = nmlop.CMP_LT

    if op == nmlop.CMP_LT:
        extra_actions.extend(parse_actionD(ParameterAssignment(assignment.param, nmlop.SUB(expr1, expr2))))
        op = nmlop.SHIFTU_LEFT  # shift left by negative number = shift right
        expr1 = assignment.param
        expr2 = expression.ConstantNumeric(-31)

    elif op == nmlop.CMP_NEQ:
        extra_actions.extend(parse_actionD(ParameterAssignment(assignment.param, nmlop.SUB(expr1, expr2))))
        op = nmlop.DIV
        # We rely here on the (ondocumented) behavior of both OpenTTD and TTDPatch
        # that expr/0==expr. What we do is compute A/A, which will result in 1 if
        # A != 0 and in 0 if A == 0
        expr1 = assignment.param
        expr2 = assignment.param

    elif op == nmlop.CMP_EQ:
        # We compute A==B by doing not(A - B) which will result in a value != 0
        # if A is equal to B
        extra_actions.extend(parse_actionD(ParameterAssignment(assignment.param, nmlop.SUB(expr1, expr2))))
        # Clamp the value to 0/1, see above for details
        extra_actions.extend(
            parse_actionD(ParameterAssignment(assignment.param, nmlop.DIV(assignment.param, assignment.param)))
        )
        op = nmlop.SUB
        expr1 = expression.ConstantNumeric(1)
        expr2 = assignment.param

    if op == nmlop.SHIFT_RIGHT or op == nmlop.SHIFTU_RIGHT:
        if isinstance(expr2, expression.ConstantNumeric):
            expr2.value *= -1
        else:
            expr2 = nmlop.SUB(0, expr2)
        op = nmlop.SHIFT_LEFT if op == nmlop.SHIFT_RIGHT else nmlop.SHIFTU_LEFT

    elif op == nmlop.XOR:
        # a ^ b ==> (a | b) - (a & b)
        expr1 = parse_subexpression(expr1, extra_actions)
        expr2 = parse_subexpression(expr2, extra_actions)
        tmp_param1, tmp_action_list1 = get_tmp_parameter(nmlop.OR(expr1, expr2))
        tmp_param2, tmp_action_list2 = get_tmp_parameter(nmlop.AND(expr1, expr2))
        extra_actions.extend(tmp_action_list1)
        extra_actions.extend(tmp_action_list2)
        expr1 = expression.Parameter(expression.ConstantNumeric(tmp_param1))
        expr2 = expression.Parameter(expression.ConstantNumeric(tmp_param2))
        op = nmlop.SUB

    return op, expr1, expr2, extra_actions
Beispiel #5
0
def value_mul_div(mul, div):
    return lambda var, info: nmlop.DIV(nmlop.MUL(var, mul), div)
Beispiel #6
0
def compute_table(snowline):
    """
    Compute the table with snowline height for each day of the year.

    @param snowline: Snowline definition.
    @type  snowline: L{Snowline}

    @return: Table of 12*32 entries with snowline heights.
    @rtype:  C{str}
    """
    day_table = [None] * 365  # Height at each day, starting at day 0
    for dh in snowline.date_heights:
        doy = dh.name.reduce()
        if not isinstance(doy, expression.ConstantNumeric):
            raise generic.ScriptError(
                "Day of year is not a compile-time constant", doy.pos)
        if doy.value < 1 or doy.value > 365:
            raise generic.ScriptError(
                "Day of the year must be between 1 and 365", doy.pos)

        height = dh.value.reduce()
        if isinstance(height, expression.ConstantNumeric) and height.value < 0:
            raise generic.ScriptError("Height must be at least 0", height.pos)
        if dh.unit is None:
            if isinstance(height,
                          expression.ConstantNumeric) and height.value > 255:
                raise generic.ScriptError("Height must be at most 255",
                                          height.pos)
        else:
            unit = dh.unit
            if unit.type != "snowline":
                raise generic.ScriptError(
                    'Expected a snowline percentage ("snow%")', height.pos)

            if isinstance(height,
                          expression.ConstantNumeric) and height.value > 100:
                raise generic.ScriptError("Height must be at most 100 snow%",
                                          height.pos)

            mul, div = unit.convert, 1
            if isinstance(mul, tuple):
                mul, div = mul

            # Factor out common factors
            gcd = generic.greatest_common_divisor(mul, div)
            mul //= gcd
            div //= gcd

            if isinstance(
                    height,
                (expression.ConstantNumeric, expression.ConstantFloat)):
                # Even if mul == div == 1, we have to round floats and adjust value
                height = expression.ConstantNumeric(
                    int(float(height.value) * mul / div + 0.5), height.pos)
            elif mul != div:
                # Compute (value * mul + div/2) / div
                height = nmlop.MUL(height, mul)
                height = nmlop.ADD(height, int(div / 2))
                height = nmlop.DIV(height, div)

        # For 'linear' snow-line, only accept integer constants.
        if snowline.type != "equal" and not isinstance(
                height, expression.ConstantNumeric):
            raise generic.ScriptError("Height is not a compile-time constant",
                                      height.pos)

        day_table[doy.value - 1] = height

    # Find first specified point.
    start = 0
    while start < 365 and day_table[start] is None:
        start = start + 1
    if start == 365:
        raise generic.ScriptError("No heights given for the snowline table",
                                  snowline.pos)

    first_point = start
    while True:
        # Find second point from start
        end = start + 1
        if end == 365:
            end = 0

        while end != first_point and day_table[end] is None:
            end = end + 1
            if end == 365:
                end = 0

        # Fill the days between start and end (exclusive both border values)
        startvalue = day_table[start]
        endvalue = day_table[end]
        unwrapped_end = end
        if end < start:
            unwrapped_end += 365

        if snowline.type == "equal":
            for day in range(start + 1, unwrapped_end):
                if day >= 365:
                    day -= 365
                day_table[day] = startvalue
        else:
            assert snowline.type == "linear"

            if start != end:
                dhd = float(endvalue.value -
                            startvalue.value) / float(unwrapped_end - start)
            else:
                assert startvalue.value == endvalue.value
                dhd = 0

            for day in range(start + 1, unwrapped_end):
                uday = day
                if uday >= 365:
                    uday -= 365
                height = startvalue.value + int(round(dhd * (day - start)))
                day_table[uday] = expression.ConstantNumeric(height)

        if end == first_point:  # All days done
            break

        start = end

    table = [None] * (12 * 32)
    for dy in range(365):
        today = datetime.date.fromordinal(dy + 1)
        if day_table[dy]:
            expr = day_table[dy].reduce()
        else:
            expr = None
        table[(today.month - 1) * 32 + today.day - 1] = expr

    for idx, d in enumerate(table):
        if d is None:
            table[idx] = table[idx - 1]
    # Second loop is needed because we need make sure the first item is also set.
    for idx, d in enumerate(table):
        if d is None:
            table[idx] = table[idx - 1]

    return table