From b0dc5d49a81843ae5d8c7c6a9322f248727e1b20 Mon Sep 17 00:00:00 2001 From: Gabrielle Lau Date: Sat, 20 Sep 2025 04:04:36 -0700 Subject: [PATCH] Internal change PiperOrigin-RevId: 809382380 --- .../java/com/google/androidenv/MODULE.bazel | 43 ++ .../com/google/androidenv/MODULE.bazel.lock | 401 ++++++++++++++++++ .../androidenv/catch/AndroidManifest.xml | 38 ++ .../com/google/androidenv/catch/BUILD.bazel | 78 ++++ .../com/google/androidenv/catch/GameLogic.kt | 76 ++++ .../androidenv/catch/GameLogicThread.kt | 45 ++ .../google/androidenv/catch/MainActivity.kt | 152 +++++++ .../google/androidenv/catch/RenderThread.kt | 53 +++ .../androidenv/catch/res/layout/main.xml | 26 ++ .../androidenv/catch/res/values/strings.xml | 17 + .../androidenv/catch/sprite/BUILD.bazel | 66 +++ .../androidenv/catch/sprite/Background.kt | 26 ++ .../google/androidenv/catch/sprite/Ball.kt | 108 +++++ .../androidenv/catch/sprite/LineSegment.kt | 18 + .../google/androidenv/catch/sprite/Paddle.kt | 70 +++ .../google/androidenv/catch/sprite/Point.kt | 18 + .../google/androidenv/catch/sprite/Sprite.kt | 24 ++ 17 files changed, 1259 insertions(+) create mode 100644 android_env/apps/java/com/google/androidenv/MODULE.bazel create mode 100644 android_env/apps/java/com/google/androidenv/MODULE.bazel.lock create mode 100644 android_env/apps/java/com/google/androidenv/catch/AndroidManifest.xml create mode 100644 android_env/apps/java/com/google/androidenv/catch/BUILD.bazel create mode 100644 android_env/apps/java/com/google/androidenv/catch/GameLogic.kt create mode 100644 android_env/apps/java/com/google/androidenv/catch/GameLogicThread.kt create mode 100644 android_env/apps/java/com/google/androidenv/catch/MainActivity.kt create mode 100644 android_env/apps/java/com/google/androidenv/catch/RenderThread.kt create mode 100644 android_env/apps/java/com/google/androidenv/catch/res/layout/main.xml create mode 100644 android_env/apps/java/com/google/androidenv/catch/res/values/strings.xml create mode 100644 android_env/apps/java/com/google/androidenv/catch/sprite/BUILD.bazel create mode 100644 android_env/apps/java/com/google/androidenv/catch/sprite/Background.kt create mode 100644 android_env/apps/java/com/google/androidenv/catch/sprite/Ball.kt create mode 100644 android_env/apps/java/com/google/androidenv/catch/sprite/LineSegment.kt create mode 100644 android_env/apps/java/com/google/androidenv/catch/sprite/Paddle.kt create mode 100644 android_env/apps/java/com/google/androidenv/catch/sprite/Point.kt create mode 100644 android_env/apps/java/com/google/androidenv/catch/sprite/Sprite.kt diff --git a/android_env/apps/java/com/google/androidenv/MODULE.bazel b/android_env/apps/java/com/google/androidenv/MODULE.bazel new file mode 100644 index 00000000..b2ed3c31 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/MODULE.bazel @@ -0,0 +1,43 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +module( + name = "catch", + version = "0.1.0", +) + +bazel_dep(name = "rules_kotlin", version = "2.1.8") +bazel_dep(name = "rules_jvm_external", version = "6.8") + +maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") +maven.install( + artifacts = [ + "joda-time:joda-time:2.12.7", + ], + repositories = [ + "https://repo1.maven.org/maven2", + ], +) +use_repo(maven, "maven") + +bazel_dep(name = "rules_android", version = "0.6.6") + +remote_android_extensions = use_extension( + "@rules_android//bzlmod_extensions:android_extensions.bzl", + "remote_android_tools_extensions", +) +use_repo(remote_android_extensions, "android_tools") + +android_sdk_repository_extension = use_extension("@rules_android//rules/android_sdk_repository:rule.bzl", "android_sdk_repository_extension") +use_repo(android_sdk_repository_extension, "androidsdk") diff --git a/android_env/apps/java/com/google/androidenv/MODULE.bazel.lock b/android_env/apps/java/com/google/androidenv/MODULE.bazel.lock new file mode 100644 index 00000000..b3c81a88 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/MODULE.bazel.lock @@ -0,0 +1,401 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +{ + "lockFileVersion": 18, + "registryFileHashes": { + "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", + "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", + "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.2/MODULE.bazel": "73939767a4686cd9a520d16af5ab440071ed75cec1a876bf2fcfaf1f71987a16", + "https://bcr.bazel.build/modules/abseil-cpp/20250127.0/MODULE.bazel": "d1086e248cda6576862b4b3fe9ad76a214e08c189af5b42557a6e1888812c5d5", + "https://bcr.bazel.build/modules/abseil-cpp/20250127.0/source.json": "1b996859f840d8efc7c720efc61dcf2a84b1261cb3974cbbe9b6666ebf567775", + "https://bcr.bazel.build/modules/abseil-py/2.1.0/MODULE.bazel": "5ebe5bf853769c65707e5c28f216798f7a4b1042015e6a36e6d03094d94bec8a", + "https://bcr.bazel.build/modules/abseil-py/2.1.0/source.json": "0e8fc4f088ce07099c1cd6594c20c7ddbb48b4b3c0849b7d94ba94be88ff042b", + "https://bcr.bazel.build/modules/apple_support/1.11.1/MODULE.bazel": "1843d7cd8a58369a444fc6000e7304425fba600ff641592161d9f15b179fb896", + "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85", + "https://bcr.bazel.build/modules/apple_support/1.15.1/source.json": "517f2b77430084c541bc9be2db63fdcbb7102938c5f64c17ee60ffda2e5cf07b", + "https://bcr.bazel.build/modules/bazel_features/1.1.0/MODULE.bazel": "cfd42ff3b815a5f39554d97182657f8c4b9719568eb7fded2b9135f084bf760b", + "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", + "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", + "https://bcr.bazel.build/modules/bazel_features/1.13.0/MODULE.bazel": "c14c33c7c3c730612bdbe14ebbb5e61936b6f11322ea95a6e91cd1ba962f94df", + "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", + "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", + "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", + "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", + "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", + "https://bcr.bazel.build/modules/bazel_features/1.23.0/MODULE.bazel": "fd1ac84bc4e97a5a0816b7fd7d4d4f6d837b0047cf4cbd81652d616af3a6591a", + "https://bcr.bazel.build/modules/bazel_features/1.25.0/MODULE.bazel": "e2e60a10a6da64bbf533f15ca652bf61a033e41c2ed734d79a9a08ba87f68c1a", + "https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9", + "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", + "https://bcr.bazel.build/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc", + "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", + "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", + "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", + "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", + "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651", + "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", + "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953", + "https://bcr.bazel.build/modules/bazel_worker_api/0.0.1/MODULE.bazel": "02a13b77321773b2042e70ee5e4c5e099c8ddee4cf2da9cd420442c36938d4bd", + "https://bcr.bazel.build/modules/bazel_worker_api/0.0.4/MODULE.bazel": "460aa12d01231a80cce03c548287b433b321d205b0028ae596728c35e5ee442e", + "https://bcr.bazel.build/modules/bazel_worker_api/0.0.4/source.json": "d353c410d47a8b65d09fa98e83d57ebec257a2c2b9c6e42d6fda1cb25e5464a5", + "https://bcr.bazel.build/modules/bazel_worker_java/0.0.4/MODULE.bazel": "82494a01018bb7ef06d4a17ec4cd7a758721f10eb8b6c820a818e70d669500db", + "https://bcr.bazel.build/modules/bazel_worker_java/0.0.4/source.json": "a2d30458fd86cf022c2b6331e652526fa08e17573b2f5034a9dbcacdf9c2583c", + "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", + "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", + "https://bcr.bazel.build/modules/gazelle/0.32.0/MODULE.bazel": "b499f58a5d0d3537f3cf5b76d8ada18242f64ec474d8391247438bf04f58c7b8", + "https://bcr.bazel.build/modules/gazelle/0.33.0/MODULE.bazel": "a13a0f279b462b784fb8dd52a4074526c4a2afe70e114c7d09066097a46b3350", + "https://bcr.bazel.build/modules/gazelle/0.34.0/MODULE.bazel": "abdd8ce4d70978933209db92e436deb3a8b737859e9354fb5fd11fb5c2004c8a", + "https://bcr.bazel.build/modules/gazelle/0.36.0/MODULE.bazel": "e375d5d6e9a6ca59b0cb38b0540bc9a05b6aa926d322f2de268ad267a2ee74c0", + "https://bcr.bazel.build/modules/gazelle/0.40.0/MODULE.bazel": "42ba5378ebe845fca43989a53186ab436d956db498acde790685fe0e8f9c6146", + "https://bcr.bazel.build/modules/gazelle/0.40.0/source.json": "1e5ef6e4d8b9b6836d93273c781e78ff829ea2e077afef7a57298040fa4f010a", + "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", + "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", + "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", + "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", + "https://bcr.bazel.build/modules/googletest/1.15.2/MODULE.bazel": "6de1edc1d26cafb0ea1a6ab3f4d4192d91a312fd2d360b63adaa213cd00b2108", + "https://bcr.bazel.build/modules/googletest/1.15.2/source.json": "dbdda654dcb3a0d7a8bc5d0ac5fc7e150b58c2a986025ae5bc634bb2cb61f470", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", + "https://bcr.bazel.build/modules/jsoncpp/1.9.6/MODULE.bazel": "2f8d20d3b7d54143213c4dfc3d98225c42de7d666011528dc8fe91591e2e17b0", + "https://bcr.bazel.build/modules/jsoncpp/1.9.6/source.json": "a04756d367a2126c3541682864ecec52f92cdee80a35735a3cb249ce015ca000", + "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", + "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74", + "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9", + "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", + "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", + "https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29", + "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", + "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", + "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", + "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", + "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", + "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", + "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", + "https://bcr.bazel.build/modules/protobuf/23.1/MODULE.bazel": "88b393b3eb4101d18129e5db51847cd40a5517a53e81216144a8c32dfeeca52a", + "https://bcr.bazel.build/modules/protobuf/24.4/MODULE.bazel": "7bc7ce5f2abf36b3b7b7c8218d3acdebb9426aeb35c2257c96445756f970eb12", + "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", + "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", + "https://bcr.bazel.build/modules/protobuf/27.2/MODULE.bazel": "32450b50673882e4c8c3d10a83f3bc82161b213ed2f80d17e38bece8f165c295", + "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", + "https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92", + "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", + "https://bcr.bazel.build/modules/protobuf/29.3/MODULE.bazel": "77480eea5fb5541903e49683f24dc3e09f4a79e0eea247414887bb9fc0066e94", + "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", + "https://bcr.bazel.build/modules/protobuf/3.19.2/MODULE.bazel": "532ffe5f2186b69fdde039efe6df13ba726ff338c6bc82275ad433013fa10573", + "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", + "https://bcr.bazel.build/modules/protobuf/31.1/MODULE.bazel": "379a389bb330b7b8c1cdf331cc90bf3e13de5614799b3b52cdb7c6f389f6b38e", + "https://bcr.bazel.build/modules/protobuf/31.1/source.json": "25af5d0219da0c0fc4d1191a24ce438e6ca7f49d2e1a94f354efeba6ef10426f", + "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", + "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34", + "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/source.json": "6900fdc8a9e95866b8c0d4ad4aba4d4236317b5c1cd04c502df3f0d33afed680", + "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", + "https://bcr.bazel.build/modules/re2/2024-07-02.bcr.1/MODULE.bazel": "b4963dda9b31080be1905ef085ecd7dd6cd47c05c79b9cdf83ade83ab2ab271a", + "https://bcr.bazel.build/modules/re2/2024-07-02.bcr.1/source.json": "2ff292be6ef3340325ce8a045ecc326e92cbfab47c7cbab4bd85d28971b97ac4", + "https://bcr.bazel.build/modules/re2/2024-07-02/MODULE.bazel": "0eadc4395959969297cbcf31a249ff457f2f1d456228c67719480205aa306daa", + "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8", + "https://bcr.bazel.build/modules/rules_android/0.6.4/MODULE.bazel": "b4cde12d506dd65d82b2be39761f49f5797303343a3d5b4ee191c0cdf9ef387c", + "https://bcr.bazel.build/modules/rules_android/0.6.6/MODULE.bazel": "b0fb569752aab65ab1a9db0a8f6cfaf5aa1754965e17e95dcf0e4d88e192a68d", + "https://bcr.bazel.build/modules/rules_android/0.6.6/source.json": "a9d8dc2d5a102dc03269a94acc886a4cab82cdcb9ccbc77b0f665d6d17a6ae09", + "https://bcr.bazel.build/modules/rules_apple/3.16.0/MODULE.bazel": "0d1caf0b8375942ce98ea944be754a18874041e4e0459401d925577624d3a54a", + "https://bcr.bazel.build/modules/rules_apple/3.16.0/source.json": "d8b5fe461272018cc07cfafce11fe369c7525330804c37eec5a82f84cd475366", + "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", + "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", + "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", + "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", + "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", + "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", + "https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a", + "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", + "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", + "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", + "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", + "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", + "https://bcr.bazel.build/modules/rules_cc/0.1.1/source.json": "d61627377bd7dd1da4652063e368d9366fc9a73920bfa396798ad92172cf645c", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", + "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", + "https://bcr.bazel.build/modules/rules_go/0.41.0/MODULE.bazel": "55861d8e8bb0e62cbd2896f60ff303f62ffcb0eddb74ecb0e5c0cbe36fc292c8", + "https://bcr.bazel.build/modules/rules_go/0.42.0/MODULE.bazel": "8cfa875b9aa8c6fce2b2e5925e73c1388173ea3c32a0db4d2b4804b453c14270", + "https://bcr.bazel.build/modules/rules_go/0.46.0/MODULE.bazel": "3477df8bdcc49e698b9d25f734c4f3a9f5931ff34ee48a2c662be168f5f2d3fd", + "https://bcr.bazel.build/modules/rules_go/0.50.1/MODULE.bazel": "b91a308dc5782bb0a8021ad4330c81fea5bda77f96b9e4c117b9b9c8f6665ee0", + "https://bcr.bazel.build/modules/rules_go/0.51.0-rc2/MODULE.bazel": "edfc3a9cea7bedb0eaaff37b0d7817c1a4bf72b3c615580b0ffcee6c52690fd4", + "https://bcr.bazel.build/modules/rules_go/0.51.0-rc2/source.json": "6b5cd0b3da2bd0e6949580851db990a04af0a285f072b9a0f059424457cd8cc9", + "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", + "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", + "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", + "https://bcr.bazel.build/modules/rules_java/6.3.0/MODULE.bazel": "a97c7678c19f236a956ad260d59c86e10a463badb7eb2eda787490f4c969b963", + "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6", + "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", + "https://bcr.bazel.build/modules/rules_java/7.1.0/MODULE.bazel": "30d9135a2b6561c761bd67bd4990da591e6bdc128790ce3e7afd6a3558b2fb64", + "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", + "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", + "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", + "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", + "https://bcr.bazel.build/modules/rules_java/7.4.0/MODULE.bazel": "a592852f8a3dd539e82ee6542013bf2cadfc4c6946be8941e189d224500a8934", + "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", + "https://bcr.bazel.build/modules/rules_java/8.12.0/MODULE.bazel": "8e6590b961f2defdfc2811c089c75716cb2f06c8a4edeb9a8d85eaa64ee2a761", + "https://bcr.bazel.build/modules/rules_java/8.13.0/MODULE.bazel": "0444ebf737d144cf2bb2ccb368e7f1cce735264285f2a3711785827c1686625e", + "https://bcr.bazel.build/modules/rules_java/8.13.0/source.json": "4605c0f676b87dd9d1fabd4d743b71f04d97503bd1a79aad53f87399fb5396de", + "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", + "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", + "https://bcr.bazel.build/modules/rules_java/8.6.0/MODULE.bazel": "9c064c434606d75a086f15ade5edb514308cccd1544c2b2a89bbac4310e41c71", + "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2", + "https://bcr.bazel.build/modules/rules_java/8.6.3/MODULE.bazel": "e90505b7a931d194245ffcfb6ff4ca8ef9d46b4e830d12e64817752e0198e2ed", + "https://bcr.bazel.build/modules/rules_java/8.9.0/MODULE.bazel": "e17c876cb53dcd817b7b7f0d2985b710610169729e8c371b2221cacdcd3dce4a", + "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", + "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", + "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", + "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d", + "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4", + "https://bcr.bazel.build/modules/rules_jvm_external/6.2/MODULE.bazel": "36a6e52487a855f33cb960724eb56547fa87e2c98a0474c3acad94339d7f8e99", + "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", + "https://bcr.bazel.build/modules/rules_jvm_external/6.6/MODULE.bazel": "153042249c7060536dc95b6bb9f9bb8063b8a0b0cb7acdb381bddbc2374aed55", + "https://bcr.bazel.build/modules/rules_jvm_external/6.7/MODULE.bazel": "e717beabc4d091ecb2c803c2d341b88590e9116b8bf7947915eeb33aab4f96dd", + "https://bcr.bazel.build/modules/rules_jvm_external/6.8/MODULE.bazel": "b5afe861e867e4c8e5b88e401cb7955bd35924258f97b1862cc966cbcf4f1a62", + "https://bcr.bazel.build/modules/rules_jvm_external/6.8/source.json": "c85e553d5ac17f7825cd85b9cceb500c64f9e44f0e93c7887469e430c4ae9eff", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.5/MODULE.bazel": "043a16a572f610558ec2030db3ff0c9938574e7dd9f58bded1bb07c0192ef025", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", + "https://bcr.bazel.build/modules/rules_kotlin/2.1.8/MODULE.bazel": "e832456b08f4bdf23a466ae4ec424ce700d4ccaa2c9e83912d32791ade43b77d", + "https://bcr.bazel.build/modules/rules_kotlin/2.1.8/source.json": "4ccee7dee20c21546ae4ea3d26d4f9c859fc906fa4c8e317d571b62d8048ae10", + "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", + "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", + "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", + "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", + "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a", + "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", + "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", + "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483", + "https://bcr.bazel.build/modules/rules_proto/6.0.0/MODULE.bazel": "b531d7f09f58dce456cd61b4579ce8c86b38544da75184eadaf0a7cb7966453f", + "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1", + "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", + "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", + "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", + "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", + "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", + "https://bcr.bazel.build/modules/rules_python/0.33.2/MODULE.bazel": "3e036c4ad8d804a4dad897d333d8dce200d943df4827cb849840055be8d2e937", + "https://bcr.bazel.build/modules/rules_python/0.37.1/MODULE.bazel": "3faeb2d9fa0a81f8980643ee33f212308f4d93eea4b9ce6f36d0b742e71e9500", + "https://bcr.bazel.build/modules/rules_python/0.37.2/MODULE.bazel": "b5ffde91410745750b6c13be1c5dc4555ef5bc50562af4a89fd77807fdde626a", + "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", + "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", + "https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43", + "https://bcr.bazel.build/modules/rules_python/1.0.0/source.json": "b0162a65c6312e45e7912e39abd1a7f8856c2c7e41ecc9b6dc688a6f6400a917", + "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/MODULE.bazel": "d44fec647d0aeb67b9f3b980cf68ba634976f3ae7ccd6c07d790b59b87a4f251", + "https://bcr.bazel.build/modules/rules_robolectric/4.14.1.2/source.json": "37c10335f2361c337c5c1f34ed36d2da70534c23088062b33a8bdaab68aa9dea", + "https://bcr.bazel.build/modules/rules_shell/0.1.2/MODULE.bazel": "66e4ca3ce084b04af0b9ff05ff14cab4e5df7503973818bb91cbc6cda08d32fc", + "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", + "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", + "https://bcr.bazel.build/modules/rules_shell/0.3.0/source.json": "c55ed591aa5009401ddf80ded9762ac32c358d2517ee7820be981e2de9756cf3", + "https://bcr.bazel.build/modules/rules_swift/1.16.0/MODULE.bazel": "4a09f199545a60d09895e8281362b1ff3bb08bbde69c6fc87aff5b92fcc916ca", + "https://bcr.bazel.build/modules/rules_swift/2.1.1/MODULE.bazel": "494900a80f944fc7aa61500c2073d9729dff0b764f0e89b824eb746959bc1046", + "https://bcr.bazel.build/modules/rules_swift/2.1.1/source.json": "40fc69dfaac64deddbb75bd99cdac55f4427d9ca0afbe408576a65428427a186", + "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", + "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", + "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", + "https://bcr.bazel.build/modules/stardoc/0.6.2/MODULE.bazel": "7060193196395f5dd668eda046ccbeacebfd98efc77fed418dbe2b82ffaa39fd", + "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", + "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", + "https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5", + "https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216", + "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91", + "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/source.json": "32bd87e5f4d7acc57c5b2ff7c325ae3061d5e242c0c4c214ae87e0f1c13e54cb", + "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", + "https://bcr.bazel.build/modules/upb/0.0.0-20230516-61a97ef/MODULE.bazel": "c0df5e35ad55e264160417fd0875932ee3c9dda63d9fccace35ac62f45e1b6f9", + "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", + "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", + "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198" + }, + "selectedYankedVersions": {}, + "moduleExtensions": { + "@@apple_support+//crosstool:setup.bzl%apple_cc_configure_extension": { + "general": { + "bzlTransitiveDigest": "E970FlMbwpgJPdPUQzatKh6BMfeE0ZpWABvwshh7Tmg=", + "usagesDigest": "EJfdUgbJdIRboG70S9dz8HhGVkL2/s3qFQ9xht8y2Rs=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_apple_cc_toolchains": { + "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf_toolchains", + "attributes": {} + }, + "local_config_apple_cc": { + "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "apple_support+", + "bazel_tools", + "bazel_tools" + ], + [ + "bazel_tools", + "rules_cc", + "rules_cc+" + ] + ] + } + }, + "@@rules_android+//bzlmod_extensions:com_android_dex.bzl%com_android_dex_extension": { + "general": { + "bzlTransitiveDigest": "aDSxoLkIA0GyiHP6VPgxLuAWEmulTMkYgxvB/P0Q42g=", + "usagesDigest": "toF8IFMu98H/VU2p1sfVC5fVXVYJunpbbmtM6tOsQXY=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_android_dex": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://android.googlesource.com/platform/dalvik/+archive/5a81c499a569731e2395f7c8d13c0e0d4e17a2b6.tar.gz", + "build_file": "@@rules_android+//bzlmod_extensions:com_android_dex.BUILD" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_android+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_android+//rules/android_sdk_repository:rule.bzl%android_sdk_repository_extension": { + "general": { + "bzlTransitiveDigest": "NAy+0M15JNVEBb8Tny6t7j3lKqTnsAMjoBB6LJ+C370=", + "usagesDigest": "et4mxrOHd+gUFXmyNrWCFDGIV7L95LyTpnGVEy2wvDE=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "androidsdk": { + "repoRuleId": "@@rules_android+//rules/android_sdk_repository:rule.bzl%_android_sdk_repository", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [] + } + }, + "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { + "general": { + "bzlTransitiveDigest": "CgSFQ7VRhs6G8nojJKNB6szAhYnHEblrCU/AruTOxtw=", + "usagesDigest": "QPppUlwb7NSBhcaYae+JZPqTEmJKCkOXKFPXQS7aAJE=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_github_jetbrains_kotlin_git": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository", + "attributes": { + "urls": [ + "https://github.com/JetBrains/kotlin/releases/download/v2.1.21/kotlin-compiler-2.1.21.zip" + ], + "sha256": "1ba08a8b45da99339a0601134cc037b54cf85e9bc0edbe76dcbd27c2d684a977" + } + }, + "com_github_jetbrains_kotlin": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository", + "attributes": { + "git_repository_name": "com_github_jetbrains_kotlin_git", + "compiler_version": "2.1.21" + } + }, + "com_github_google_ksp": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository", + "attributes": { + "urls": [ + "https://github.com/google/ksp/releases/download/2.1.21-2.0.1/artifacts.zip" + ], + "sha256": "44e965bb067b2bb5cd9184dab2c3dea6e3eab747d341c07645bb4c88f09e49c8", + "strip_version": "2.1.21-2.0.1" + } + }, + "com_github_pinterest_ktlint": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", + "attributes": { + "sha256": "5ba1ac917a06b0f02daaa60d10abbedd2220d60216af670c67a45b91c74cf8bb", + "urls": [ + "https://github.com/pinterest/ktlint/releases/download/1.6.0/ktlint" + ], + "executable": true + } + }, + "kotlinx_serialization_core_jvm": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "sha256": "3565b6d4d789bf70683c45566944287fc1d8dc75c23d98bd87d01059cc76f2b3", + "urls": [ + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-core-jvm/1.8.1/kotlinx-serialization-core-jvm-1.8.1.jar" + ] + } + }, + "kotlinx_serialization_json": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "sha256": "58adf3358a0f99dd8d66a550fbe19064d395e0d5f7f1e46515cd3470a56fbbb0", + "urls": [ + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json/1.8.1/kotlinx-serialization-json-1.8.1.jar" + ] + } + }, + "kotlinx_serialization_json_jvm": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "sha256": "8769e5647557e3700919c32d508f5c5dad53c5d8234cd10846354fbcff14aa24", + "urls": [ + "https://repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-serialization-json-jvm/1.8.1/kotlinx-serialization-json-jvm-1.8.1.jar" + ] + } + }, + "kotlin_build_tools_impl": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "sha256": "6e94896e321603e3bfe89fef02478e44d1d64a3d25d49d0694892ffc01c60acf", + "urls": [ + "https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-build-tools-impl/2.1.20/kotlin-build-tools-impl-2.1.20.jar" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_kotlin+", + "bazel_tools", + "bazel_tools" + ] + ] + } + } + } +} diff --git a/android_env/apps/java/com/google/androidenv/catch/AndroidManifest.xml b/android_env/apps/java/com/google/androidenv/catch/AndroidManifest.xml new file mode 100644 index 00000000..4ded8b50 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/AndroidManifest.xml @@ -0,0 +1,38 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + + + + + + + + + + + diff --git a/android_env/apps/java/com/google/androidenv/catch/BUILD.bazel b/android_env/apps/java/com/google/androidenv/catch/BUILD.bazel new file mode 100644 index 00000000..3c071963 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/BUILD.bazel @@ -0,0 +1,78 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +# Classic RL task implemented as an Android app. + +load("@rules_android//rules:rules.bzl", "android_binary") +load("@rules_kotlin//kotlin:android.bzl", "kt_android_library") + +package( + default_applicable_licenses = ["//third_party/py/android_env:license"], + default_visibility = ["//catch:__subpackages__"], +) + +licenses(["notice"]) + +# Build targets will slowly be added to this project and ultimately we'll have +# an android_binary() target that'll contain the app. +kt_android_library( + name = "GameLogic", + srcs = ["GameLogic.kt"], + deps = [ + "//catch/sprite:Background", + "//catch/sprite:Ball", + "//catch/sprite:LineSegment", + "//catch/sprite:Paddle", + "@maven//:joda_time_joda_time", + ], +) + +kt_android_library( + name = "RenderThread", + srcs = ["RenderThread.kt"], + deps = [ + ":GameLogic", + "@maven//:joda_time_joda_time", + ], +) + +kt_android_library( + name = "MainActivity", + srcs = ["MainActivity.kt"], + manifest = "AndroidManifest.xml", + resource_files = glob(["res/**"]), + deps = [ + ":GameLogic", + ":GameLogicThread", + ":RenderThread", + "//catch/sprite:Background", + "//catch/sprite:Ball", + "//catch/sprite:Paddle", + ], +) + +android_binary( + name = "app", + manifest = "AndroidManifest.xml", + multidex = "native", + deps = [":MainActivity"], +) + +kt_android_library( + name = "GameLogicThread", + srcs = ["GameLogicThread.kt"], + deps = [ + ":GameLogic", + ], +) diff --git a/android_env/apps/java/com/google/androidenv/catch/GameLogic.kt b/android_env/apps/java/com/google/androidenv/catch/GameLogic.kt new file mode 100644 index 00000000..7b299f1d --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/GameLogic.kt @@ -0,0 +1,76 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidenv.catch + +import android.graphics.Canvas +import android.view.MotionEvent +import com.google.androidenv.catch.sprite.Background +import com.google.androidenv.catch.sprite.Ball +import com.google.androidenv.catch.sprite.LineSegment +import com.google.androidenv.catch.sprite.Paddle +import kotlin.random.Random +import org.joda.time.Duration +import org.joda.time.Instant + +/** The class that contains the game logic. */ +open class GameLogic( + // Expected number of frames per second. + fps: Int = 60, + // Pseudo random number generator. + private val rand: Random = Random.Default, + // Width and height of the game in pixels. + private val width: Int, + private val height: Int, + // UI objects in the game. + private var background: Background = Background(), + private var ball: Ball = Ball(maxX = width, maxY = height, rand = rand), + private var paddle: Paddle = Paddle(maxX = width, y = height), +) { + + private val sleepTime: Duration = Duration.millis((1000.0 / fps).toLong()) + + /** Reinitializes the state of the game. */ + // Need to make this open to allow for testing. + open fun reset() { + this.ball.reset() + } + + /** Runs one "throw" of a [ball] that needs to be caught by the [paddle]. */ + // Need to make this open to allow for testing. + open fun run(): Boolean { + var lastTimestamp = Instant.now() + do { + Thread.sleep(sleepTime.millis) + val now = Instant.now() + val interval = Duration(lastTimestamp, now) + lastTimestamp = now + ball.update(interval) + } while (!ball.isOutOfBounds()) + + return ball.intersects(LineSegment(paddle.topLeft(), paddle.topRight())) + } + + /** Processes a user event (e.g. a touchscreen event) and updates the [paddle] accordingly. */ + fun handleTouch(event: MotionEvent) { + paddle.x = event.x.toInt() + } + + /** Renders the game on [c]. */ + open fun render(c: Canvas) { + background.draw(c) + ball.draw(c) + paddle.draw(c) + } +} diff --git a/android_env/apps/java/com/google/androidenv/catch/GameLogicThread.kt b/android_env/apps/java/com/google/androidenv/catch/GameLogicThread.kt new file mode 100644 index 00000000..231d481b --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/GameLogicThread.kt @@ -0,0 +1,45 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidenv.catch + +import android.util.Log + +/** A thread that continuously runs the game logic, resetting after each internal [run()]. */ +class GameLogicThread(private val game: GameLogic, private val loggingTag: String) : Thread() { + + /** Whether this thread should continuously run. */ + private var shouldRun: Boolean = true + /** A counter of game runs. */ + private var counter: Int = 0 + + /** + * Lets the current [run()] iteration complete then break exit this [Thread]. + * + * Notice that [shouldRun] cannot have a private getter with a public setter (please see + * https://youtrack.jetbrains.com/issue/KT-3110 for details), hence this public function. Also + * notice that we cannot call this function [stop()] since it would shadow [Thread.stop()]. + */ + public fun finish() { + shouldRun = false + } + + /** Continuously runs the [game] until [finish()] is called. */ + public override fun run() { + while (shouldRun) { + game.reset() + Log.i(loggingTag, "${counter++} - ${game.run()}") + } + } +} diff --git a/android_env/apps/java/com/google/androidenv/catch/MainActivity.kt b/android_env/apps/java/com/google/androidenv/catch/MainActivity.kt new file mode 100644 index 00000000..41eb3b25 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/MainActivity.kt @@ -0,0 +1,152 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidenv.catch + +import android.app.Activity +import android.content.Intent +import android.graphics.Color +import android.os.Bundle +import android.util.Log +import android.view.SurfaceHolder +import android.view.SurfaceView +import android.view.View +import android.view.Window +import com.google.androidenv.catch.sprite.Background +import com.google.androidenv.catch.sprite.Ball +import com.google.androidenv.catch.sprite.Paddle + +/** The activity that allows users to play the RL game of Catch. */ +class MainActivity() : Activity(), SurfaceHolder.Callback { + + private var surfaceView: SurfaceView? = null + private var renderThread: RenderThread? = null + private var gameLogicThread: GameLogicThread? = null + + private val fps: Int = 60 + private var gameCounter: Int = 0 + private var width: Int = -1 + private var height: Int = -1 + + private var extras: Bundle? = null + + // [Activity] overrides. + + /** Initializes the Android [View] and sets up callbacks. */ + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Log.i(TAG, "MainActivity::onCreate()") + requestWindowFeature(Window.FEATURE_NO_TITLE) + setContentView(R.layout.main) + val surface: SurfaceView? = findViewById(R.id.surfaceView) + if (surface == null) throw Exception("Could not create SurfaceView. Aborting...") + + surface.visibility = View.VISIBLE + surface.holder.addCallback(this) + surfaceView = surface + extras = intent?.extras + } + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + Log.i(TAG, "MainActivity::onNewIntent()") + extras = intent?.extras + startGame() + } + + // [SurfaceHolder.Callback] overrides. + + override fun surfaceCreated(holder: SurfaceHolder) { + Log.i(TAG, "MainActivity::surfaceCreated()") + renderThread = RenderThread(surfaceHolder = holder, fps = fps).also { it.start() } + } + + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + Log.i(TAG, "MainActivity::surfaceChanged()") + this.width = width + this.height = height + startGame() + } + + override fun surfaceDestroyed(holder: SurfaceHolder) { + Log.i(TAG, "MainActivity::surfaceDestroyed()") + renderThread?.finish() + renderThread?.join() + gameLogicThread?.finish() + gameLogicThread?.join() + } + + private fun startGame() { + Log.i(TAG, "MainActivity::startGame()") + if (width <= 0 || height <= 0) { + Log.e(TAG, "MainActivity::startGame() - Width or height not initialized yet.") + return + } + val backgroundColor = Color.parseColor(extras?.getString("backgroundColor") ?: "BLACK") + val ballColor = Color.parseColor(extras?.getString("ballColor") ?: "WHITE") + val ballRadius = extras?.getFloat("ballRadius", 10.0f) ?: 10.0f + val ballSpeed = extras?.getFloat("ballSpeed", 0.2f) ?: 0.2f + val paddleColor = Color.parseColor(extras?.getString("paddleColor") ?: "WHITE") + val paddleWidth = extras?.getInt("paddleWidth", 80) ?: 80 + val paddleHeight = extras?.getInt("paddleHeight", 10) ?: 10 + Log.i(TAG, "MainActivity::startGame() - extras bundle: $extras") + val game = + GameLogic( + width = width, + height = height, + fps = fps, + background = Background(color = backgroundColor), + ball = + Ball( + maxX = width, + maxY = height, + color = ballColor, + radius = ballRadius, + speed = ballSpeed, + ), + paddle = + Paddle( + color = paddleColor, + width = paddleWidth, + height = paddleHeight, + maxX = width, + y = (height - paddleHeight / 2), + ), + ) + + // Stop the previous game logic thread if it's running. + gameLogicThread?.finish() + gameLogicThread?.join() + + // Create and start the new GameLogicThread, passing the game instance. + gameLogicThread = GameLogicThread(game, TAG).also { it.start() } + + // Pass the same game instance to the render thread. + renderThread?.game = game + + surfaceView?.setOnTouchListener( + // Suppress warning for ClickableViewAccessibility since click handling + // is not within an OnTouchListener. + @SuppressWarnings("ClickableViewAccessibility") + View.OnTouchListener { _, motionEvent -> + game.handleTouch(motionEvent) + true + } + ) + } + + companion object { + private const val TAG = "AndroidRLTask" + } +} diff --git a/android_env/apps/java/com/google/androidenv/catch/RenderThread.kt b/android_env/apps/java/com/google/androidenv/catch/RenderThread.kt new file mode 100644 index 00000000..089b2e41 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/RenderThread.kt @@ -0,0 +1,53 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidenv.catch + +import android.graphics.Canvas +import android.view.SurfaceHolder +import org.joda.time.Duration + +/** A thread that continuously renders the game logic onto a surface. */ +class RenderThread(private val surfaceHolder: SurfaceHolder, private val fps: Int = 60) : Thread() { + + /** Whether this thread should continuously run. */ + private var shouldRun: Boolean = true + /** How long to sleep at each [run()] iteration. */ + private val sleepTime: Duration = Duration.millis((1000.0 / fps).toLong()) + /** The class responsible for issuing rendering commands to the canvas. */ + var game: GameLogic? = null + + /** + * Runs the current game logic [run()] to completion. + * + * Notice that [shouldRun] cannot have a private getter with a public setter (please see + * https://youtrack.jetbrains.com/issue/KT-3110 for details), hence this public function. Also + * notice that we cannot call this function [stop()] since it would shadow [Thread.stop()]. + */ + public fun finish() { + shouldRun = false + } + + /** Continuously renders the [game] onto [surfaceHolder]. */ + public override fun run() { + while (shouldRun) { + if (surfaceHolder.surface?.isValid() ?: false) { + val c: Canvas = surfaceHolder.lockCanvas() + game?.render(c) + surfaceHolder.unlockCanvasAndPost(c) + } + Thread.sleep(sleepTime.millis) + } + } +} diff --git a/android_env/apps/java/com/google/androidenv/catch/res/layout/main.xml b/android_env/apps/java/com/google/androidenv/catch/res/layout/main.xml new file mode 100644 index 00000000..9eb935bd --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/res/layout/main.xml @@ -0,0 +1,26 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + + + diff --git a/android_env/apps/java/com/google/androidenv/catch/res/values/strings.xml b/android_env/apps/java/com/google/androidenv/catch/res/values/strings.xml new file mode 100644 index 00000000..bf118719 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/res/values/strings.xml @@ -0,0 +1,17 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + Catch + diff --git a/android_env/apps/java/com/google/androidenv/catch/sprite/BUILD.bazel b/android_env/apps/java/com/google/androidenv/catch/sprite/BUILD.bazel new file mode 100644 index 00000000..3bed75c6 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/sprite/BUILD.bazel @@ -0,0 +1,66 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +load("@rules_kotlin//kotlin:android.bzl", "kt_android_library") + +# Sprites for the app. + +package( + default_applicable_licenses = ["//third_party/py/android_env:license"], + default_visibility = ["//catch:__subpackages__"], +) + +licenses(["notice"]) + +kt_android_library( + name = "Background", + srcs = ["Background.kt"], + deps = [":Sprite"], +) + +kt_android_library( + name = "Ball", + srcs = ["Ball.kt"], + deps = [ + ":LineSegment", + ":Point", + ":Sprite", + "@maven//:joda_time_joda_time", + ], +) + +kt_android_library( + name = "LineSegment", + srcs = ["LineSegment.kt"], + deps = [":Point"], +) + +kt_android_library( + name = "Paddle", + srcs = ["Paddle.kt"], + deps = [ + ":Point", + ":Sprite", + ], +) + +kt_android_library( + name = "Point", + srcs = ["Point.kt"], +) + +kt_android_library( + name = "Sprite", + srcs = ["Sprite.kt"], +) diff --git a/android_env/apps/java/com/google/androidenv/catch/sprite/Background.kt b/android_env/apps/java/com/google/androidenv/catch/sprite/Background.kt new file mode 100644 index 00000000..a985b3e4 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/sprite/Background.kt @@ -0,0 +1,26 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidenv.catch.sprite + +import android.graphics.Canvas +import android.graphics.Color + +/** Represents the static background behind all objects. */ +open class Background(private val color: Int = Color.BLACK) : Sprite() { + /** Paints the canvas with the color given in the constructor. */ + override fun draw(c: Canvas) { + c.drawColor(color) + } +} diff --git a/android_env/apps/java/com/google/androidenv/catch/sprite/Ball.kt b/android_env/apps/java/com/google/androidenv/catch/sprite/Ball.kt new file mode 100644 index 00000000..6396c2dc --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/sprite/Ball.kt @@ -0,0 +1,108 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidenv.catch.sprite + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import kotlin.math.ceil +import kotlin.math.sqrt +import kotlin.random.Random +import org.joda.time.Duration + +/** Represents a ball that travels down in space with constant speed. */ +open class Ball( + private val maxX: Int, + private val maxY: Int, + private val color: Int = Color.WHITE, + private val radius: Float = 10.0f, + // `speed`'s unit is in pixels/ms. + private val speed: Float = 1.0f, + private val rand: Random = Random.Default, +) : Sprite() { + + // `x` and `y` represent the position of the center of this ball. + // + // Valid range [0, maxX]. 0==left, maxX==right. + private var x: Int = rand.nextInt(maxX) + // Valid range [0, maxY]. 0==top, maxY==bottom. + private var y: Int = ceil(radius).toInt() + + private val paint: Paint = + Paint(Paint.ANTI_ALIAS_FLAG).apply { + style = Paint.Style.FILL + color = (this@Ball).color + } + + /** Returns `true` if this ball intersects the given line [segment]. */ + fun intersects(segment: LineSegment): Boolean { + + /** A vector with two components. */ + data class Vector2D(val u: Int, val v: Int) { + /** Returns the dot product between two 2D vectors. */ + fun dot(other: Vector2D): Int = u * other.u + v * other.v + } + + /** Returns the vector representing [p] minus [q]. */ + fun pointDiff(p: Point, q: Point): Vector2D = Vector2D(p.x - q.x, p.y - q.y) + + val direction = pointDiff(segment.p1, segment.p0) // p0 -> p1. + val centerToP = pointDiff(segment.p0, Point(x, y)) // Ball center -> p0. + + // The `(centerToP + m * direction)` function models all the points in the line segment where + // the independent variable `m` is a real number in [0,1]. Putting this function into the + // formula for the circle (x ^ 2 + y ^ 2 = radius ^ 2) gives a quadratic equation + // (am^2 + bm + c = 0) where: + // [a] = direction · direction + // [b] = 2 centerToP · direction + // [c] = centerToP · centerToP - radius ^ 2 + val a = direction.dot(direction) + val b = 2 * centerToP.dot(direction) + val c = centerToP.dot(centerToP) - radius * radius + + val delta = b * b - 4 * a * c + if (delta < 0) + return false // No real roots means the (infinite) line does not intersect the ball. + + val d = sqrt(delta) + val m1 = (-b - d) / (2 * a) + val m2 = (-b + d) / (2 * a) + + // If a root is in [0,1], the line segment intersects the circumference. + // If [m1] < 0 and [m2] > 1, the line segment is "within" the circle meaning the circle + // intersects the infinite line, but not the line segment. In this case, we consider that it + // touched the ball. + return (m1 >= 0 && m1 <= 1) || (m2 >= 0 && m2 <= 1) || (m1 < 0 && m2 > 1) + } + + /** Places the ball at the top of the screen at a random x-coordinate. */ + fun reset() { + x = rand.nextInt(maxX) + y = ceil(radius).toInt() + } + + /** Moves the ball down by [timeDeltaMs]. */ + open fun update(timeDelta: Duration) { + y += (speed * timeDelta.millis).toInt() + } + + /** Returns whether the ball is over [maxY]. */ + fun isOutOfBounds(): Boolean = y + radius > maxY || y - radius < 0 + + /** Draws this ball in `c`. */ + override fun draw(c: Canvas) { + c.drawCircle(x.toFloat(), y.toFloat(), radius, paint) + } +} diff --git a/android_env/apps/java/com/google/androidenv/catch/sprite/LineSegment.kt b/android_env/apps/java/com/google/androidenv/catch/sprite/LineSegment.kt new file mode 100644 index 00000000..54807cb3 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/sprite/LineSegment.kt @@ -0,0 +1,18 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidenv.catch.sprite + +/** Represents a finite line segment in 2D connected by two points [p0] and [p1]. */ +data class LineSegment(val p0: Point, val p1: Point) diff --git a/android_env/apps/java/com/google/androidenv/catch/sprite/Paddle.kt b/android_env/apps/java/com/google/androidenv/catch/sprite/Paddle.kt new file mode 100644 index 00000000..fb57001a --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/sprite/Paddle.kt @@ -0,0 +1,70 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidenv.catch.sprite + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Rect +import kotlin.ranges.coerceIn + +/** Represents a paddle to hit/catch a falling ball. */ +open class Paddle( + private val color: Int = Color.WHITE, + // Width and height in pixels. + private val width: Int = 80, + private val height: Int = 10, + // maxX is the maximum X value for the center of the paddle. + private val maxX: Int = 100, + // The vertical position of the center of this paddle in pixels. + val y: Int = 100, +) : Sprite() { + + // Memoize a few things to make [draw()] a bit faster. + private val halfH = height / 2 + private val halfW = width / 2 + private val paint = + Paint(Paint.ANTI_ALIAS_FLAG).apply { + style = Paint.Style.FILL + color = (this@Paddle).color + } + + // The horizontal center of the paddle. + var x: Int = maxX / 2 // Start in the middle. + set(value) { + field = value.coerceIn(0, maxX) + } + + /** Returns the (x,y) coordinates of the top-left corner. */ + fun topLeft(): Point = Point(x - halfW, y - halfH) + + /** Returns the (x,y) coordinates of the top-right corner. */ + fun topRight(): Point = Point(x + halfW, y - halfH) + + fun move(deltaX: Int) { + x += deltaX + } + + override fun draw(c: Canvas) { + val rect = + Rect().apply { + bottom = y + halfH + top = y - halfH + left = x - halfW + right = x + halfW + } + c.drawRect(rect, paint) + } +} diff --git a/android_env/apps/java/com/google/androidenv/catch/sprite/Point.kt b/android_env/apps/java/com/google/androidenv/catch/sprite/Point.kt new file mode 100644 index 00000000..adb59443 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/sprite/Point.kt @@ -0,0 +1,18 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidenv.catch.sprite + +/** Represents a cartesian point in 2D. */ +data class Point(val x: Int, val y: Int) diff --git a/android_env/apps/java/com/google/androidenv/catch/sprite/Sprite.kt b/android_env/apps/java/com/google/androidenv/catch/sprite/Sprite.kt new file mode 100644 index 00000000..09520281 --- /dev/null +++ b/android_env/apps/java/com/google/androidenv/catch/sprite/Sprite.kt @@ -0,0 +1,24 @@ +// Copyright 2025 DeepMind Technologies Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.androidenv.catch.sprite + +import android.graphics.Canvas + +/** Represents something that can be drawn on the screen. */ +open class Sprite { + + /** Draws the Sprite in the given canvas. */ + open fun draw(c: Canvas) {} +}