Repository: TheAlgorithms/Python

Issue:
horizontal motion code physics
### **Describe your change:**
Added code to calculate time, height and distance covered in horizontal projectile motion problem in physics.


* [x] Add an algorithm?
* [ ] Fix a bug or typo in an existing algorithm?
* [ ] Documentation change?

### **Checklist:**
* [x] I have read [CONTRIBUTING.md](https://github.com/TheAlgorithms/Python/blob/master/CONTRIBUTING.md).
* [x] This pull request is all my own work -- I have not plagiarized.
* [x] I know that pull requests will not be merged if they fail the automated tests.
* [x] This PR only changes one algorithm file.  To ease review, please open separate PRs for separate algorithms.
* [x] All new Python files are placed inside an existing directory.
* [x] All filenames are in all lowercase characters with no spaces or dashes.
* [x] All functions and variable names follow Python naming conventions.
* [x] All function parameters and return values are annotated with Python [type hints](https://docs.python.org/3/library/typing.html).
* [x] All functions have [doctests](https://docs.python.org/3/library/doctest.html) that pass the automated testing.
* [x] All new algorithms have a URL in its comments that points to Wikipedia or other similar explanation.
* [x] If this pull request resolves one or more open issues then the commit message contains `Fixes: #{$ISSUE_NO}`.



Discussion:

Resolving patch:
diff --git a/physics/horizontal_projectile_motion.py b/physics/horizontal_projectile_motion.py
new file mode 100644
--- /dev/null
+++ b/physics/horizontal_projectile_motion.py
@@ -0,0 +1,152 @@
+"""
+Horizontal Projectile Motion problem in physics.
+This algorithm solves a specific problem in which
+the motion starts from the ground as can be seen below:
+      (v = 0)
+        **
+       *  *
+      *    *
+     *      *
+    *        *
+   *          *
+GROUND      GROUND
+For more info: https://en.wikipedia.org/wiki/Projectile_motion
+"""
+
+# Importing packages
+from math import radians as angle_to_radians
+from math import sin
+
+# Acceleration Constant on hearth (unit m/s^2)
+g = 9.80665
+
+
+def check_args(init_velocity: float, angle: float) -> None:
+    """
+    Check that the arguments are valid
+    """
+
+    # Ensure valid instance
+    if not isinstance(init_velocity, (int, float)):
+        raise TypeError("Invalid velocity. Should be a positive number.")
+
+    if not isinstance(angle, (int, float)):
+        raise TypeError("Invalid angle. Range is 1-90 degrees.")
+
+    # Ensure valid angle
+    if angle > 90 or angle < 1:
+        raise ValueError("Invalid angle. Range is 1-90 degrees.")
+
+    # Ensure valid velocity
+    if init_velocity < 0:
+        raise ValueError("Invalid velocity. Should be a positive number.")
+
+
+def horizontal_distance(init_velocity: float, angle: float) -> float:
+    """
+    Returns the horizontal distance that the object cover
+    Formula:
+            v_0^2 * sin(2 * alpha)
+            ---------------------
+                   g
+    v_0 - initial velocity
+    alpha - angle
+    >>> horizontal_distance(30, 45)
+    91.77
+    >>> horizontal_distance(100, 78)
+    414.76
+    >>> horizontal_distance(-1, 20)
+    Traceback (most recent call last):
+        ...
+    ValueError: Invalid velocity. Should be a positive number.
+    >>> horizontal_distance(30, -20)
+    Traceback (most recent call last):
+        ...
+    ValueError: Invalid angle. Range is 1-90 degrees.
+    """
+    check_args(init_velocity, angle)
+    radians = angle_to_radians(2 * angle)
+    return round(init_velocity**2 * sin(radians) / g, 2)
+
+
+def max_height(init_velocity: float, angle: float) -> float:
+    """
+    Returns the maximum height that the object reach
+    Formula:
+            v_0^2 * sin^2(alpha)
+            --------------------
+                   2g
+    v_0 - initial velocity
+    alpha - angle
+    >>> max_height(30, 45)
+    22.94
+    >>> max_height(100, 78)
+    487.82
+    >>> max_height("a", 20)
+    Traceback (most recent call last):
+        ...
+    TypeError: Invalid velocity. Should be a positive number.
+    >>> horizontal_distance(30, "b")
+    Traceback (most recent call last):
+        ...
+    TypeError: Invalid angle. Range is 1-90 degrees.
+    """
+    check_args(init_velocity, angle)
+    radians = angle_to_radians(angle)
+    return round(init_velocity**2 * sin(radians) ** 2 / (2 * g), 2)
+
+
+def total_time(init_velocity: float, angle: float) -> float:
+    """
+    Returns total time of the motion
+    Formula:
+            2 * v_0 * sin(alpha)
+            --------------------
+                   g
+    v_0 - initial velocity
+    alpha - angle
+    >>> total_time(30, 45)
+    4.33
+    >>> total_time(100, 78)
+    19.95
+    >>> total_time(-10, 40)
+    Traceback (most recent call last):
+        ...
+    ValueError: Invalid velocity. Should be a positive number.
+    >>> total_time(30, "b")
+    Traceback (most recent call last):
+        ...
+    TypeError: Invalid angle. Range is 1-90 degrees.
+    """
+    check_args(init_velocity, angle)
+    radians = angle_to_radians(angle)
+    return round(2 * init_velocity * sin(radians) / g, 2)
+
+
+def test_motion() -> None:
+    """
+    >>> test_motion()
+    """
+    v0, angle = 25, 20
+    assert horizontal_distance(v0, angle) == 40.97
+    assert max_height(v0, angle) == 3.73
+    assert total_time(v0, angle) == 1.74
+
+
+if __name__ == "__main__":
+    from doctest import testmod
+
+    testmod()
+
+    # Get input from user
+    init_vel = float(input("Initial Velocity: ").strip())
+
+    # Get input from user
+    angle = float(input("angle: ").strip())
+
+    # Print results
+    print()
+    print("Results: ")
+    print(f"Horizontal Distance: {str(horizontal_distance(init_vel, angle))} [m]")
+    print(f"Maximum Height: {str(max_height(init_vel, angle))} [m]")
+    print(f"Total Time: {str(total_time(init_vel, angle))} [s]")


Test patch: