Commit e45cfc9b authored by rawan's avatar rawan

Initial commit - Distributed Search System

parent eb97242a
package com.search.project; package com.search.project;
import com.search.project.proto.SearchRequest;
import com.search.project.proto.SearchResponse;
import com.search.project.proto.SearchServiceGrpc;
import io.grpc.*;
import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.retry.ExponentialBackoffRetry;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.List;
import java.util.Scanner;
public class Application { public class Application {
private static final String ZK_ADDRESS = "172.29.3.101:2181"; private static final String ZK_ADDRESS = "172.29.3.101:2181";
...@@ -21,62 +13,19 @@ public class Application { ...@@ -21,62 +13,19 @@ public class Application {
ServiceRegistry registry = new ServiceRegistry(client); ServiceRegistry registry = new ServiceRegistry(client);
// بدء انتخاب القائد // 1. تشغيل العقدة كـ "عامل"
registry.startLeaderElection(() -> { new Thread(() -> {
System.out.println("[Coordinator] أنا الآن القائد والمنسق للنظام."); try { WorkerNode.start(registry); } catch (Exception e) { e.printStackTrace(); }
new Thread(() -> startSearchInterface(client, registry)).start(); }).start();
});
// تشغيل كعامل (Worker) في الخلفية دائماً
startWorkerNode(registry);
}
private static void startSearchInterface(CuratorFramework client, ServiceRegistry registry) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("\n>>> أدخل كلمة البحث (أو 'exit'): ");
String query = scanner.nextLine();
if (query.equalsIgnoreCase("exit")) break;
// 2. بدء الانتخاب
registry.startLeaderElection(() -> {
System.out.println("\n[Coordinator] مبروك! هذه العقدة أصبحت القائد الآن.");
try { try {
List<String> workers = registry.getAllWorkers(); FrontendServer.start(client, registry);
if (workers.isEmpty()) { } catch (Exception e) { e.printStackTrace(); }
System.out.println("لا يوجد عمال متصلون حالياً."); });
continue;
}
for (String workerNode : workers) {
byte[] data = client.getData().forPath("/service_registry/" + workerNode);
String address = new String(data);
String[] parts = address.split(":");
ManagedChannel channel = ManagedChannelBuilder.forAddress(parts[0], Integer.parseInt(parts[1]))
.usePlaintext().build();
SearchServiceGrpc.SearchServiceBlockingStub stub = SearchServiceGrpc.newBlockingStub(channel);
SearchResponse response = stub.processSearch(SearchRequest.newBuilder().setQuery(query).build());
System.out.println("\n[الرد من العامل " + parts[1] + "]:\n" + response.getResult());
channel.shutdown();
}
} catch (Exception e) {
System.out.println("خطأ أثناء البحث: " + e.getMessage());
}
}
}
private static void startWorkerNode(ServiceRegistry registry) throws Exception {
int port;
try (ServerSocket socket = new ServerSocket(0)) { port = socket.getLocalPort(); }
String myAddress = InetAddress.getLocalHost().getHostAddress() + ":" + port;
registry.registerToCluster(myAddress);
Server server = ServerBuilder.forPort(port)
.addService(new SearchServiceImplementation())
.build();
System.out.println("[Worker] السيرفر جاهز كعامل على البورت: " + port); Thread.currentThread().join();
server.start();
server.awaitTermination();
} }
} }
\ No newline at end of file
package com.search.project;
import com.search.project.proto.SearchResponse;
import com.sun.net.httpserver.HttpServer;
import org.apache.curator.framework.CuratorFramework;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.util.List;
public class FrontendServer {
public static void start(CuratorFramework client, ServiceRegistry registry) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(9000), 0);
server.createContext("/search", (exchange) -> {
String queryPart = exchange.getRequestURI().getQuery();
String searchTerm = (queryPart != null && queryPart.contains("query=")) ?
URLDecoder.decode(queryPart.split("query=")[1], "UTF-8") : "";
List<SearchResponse.DocumentScore> results = SearchCoordinator.performDistributedSearch(searchTerm, client, registry);
StringBuilder response = new StringBuilder();
response.append("<!DOCTYPE html><html><head><meta charset='UTF-8'>");
response.append("<title>محرك البحث الموزع</title>");
// --- التنسيقات CSS ---
response.append("<style>");
response.append("body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; margin: 0; display: flex; flex-direction: column; align-items: center; padding-top: 50px; }");
response.append(".container { background: white; padding: 40px; border-radius: 20px; box-shadow: 0 15px 35px rgba(0,0,0,0.1); width: 85%; max-width: 900px; text-align: center; }");
response.append("h1 { color: #2c3e50; font-size: 2.8em; margin-bottom: 30px; letter-spacing: -1px; }");
response.append(".search-box { display: flex; justify-content: center; gap: 10px; margin-bottom: 30px; }");
response.append("input[type='text'] { padding: 15px 25px; width: 65%; border: 2px solid #dfe6e9; border-radius: 30px; outline: none; transition: 0.3s; font-size: 16px; }");
response.append("input[type='text']:focus { border-color: #3498db; box-shadow: 0 0 10px rgba(52,152,219,0.2); }");
response.append("button { padding: 15px 30px; background-color: #3498db; color: white; border: none; border-radius: 30px; cursor: pointer; font-size: 16px; font-weight: bold; transition: 0.3s; }");
response.append("button:hover { background-color: #2980b9; transform: translateY(-2px); }");
response.append("table { width: 100%; border-collapse: collapse; margin-top: 25px; background: white; border-radius: 12px; overflow: hidden; }");
response.append("th { background-color: #34495e; color: white; padding: 15px; text-transform: uppercase; font-size: 13px; letter-spacing: 1px; }");
response.append("td { padding: 15px; border-bottom: 1px solid #f1f1f1; color: #2d3436; font-size: 15px; }");
response.append("tr:hover { background-color: #f9fbfd; }");
response.append(".rank { font-weight: bold; color: #3498db; width: 10%; }");
response.append(".doc-name { text-align: left; width: 65%; }");
response.append(".score { color: #27ae60; font-weight: bold; width: 25%; }");
response.append(".footer { margin-top: 30px; color: #636e72; font-size: 14px; }");
response.append("</style></head>");
// --- بنية الصفحة HTML ---
response.append("<body>");
response.append("<div class='container'>");
response.append("<h1>Distributed Search Engine 🚀</h1>");
// إعادة إضافة خانة الإدخال (هذا ما كان محذوفاً)
response.append("<form action='/search' method='get' class='search-box'>");
response.append("<input type='text' name='query' placeholder='أدخل كلمات البحث هنا...' value='").append(searchTerm).append("'>");
response.append("<button type='submit'>إبحث الآن</button>");
response.append("</form>");
if (!searchTerm.isEmpty()) {
response.append("<div style='text-align: left; margin-bottom: 10px; color: #7f8c8d;'>نتائج البحث عن: <b style='color: #2c3e50;'>").append(searchTerm).append("</b></div>");
if (results.isEmpty()) {
response.append("<p style='color: #e74c3c;'>للأسف، لا يوجد مستندات تطابق هذا البحث.</p>");
} else {
response.append("<table>");
response.append("<thead><tr><th>Rank</th><th>Document Name</th><th>Relevance Score (TF-IDF)</th></tr></thead>");
response.append("<tbody>");
for (int i = 0; i < results.size(); i++) {
response.append("<tr>");
response.append("<td class='rank'>#").append(i + 1).append("</td>");
response.append("<td class='doc-name'>").append(results.get(i).getDocName()).append("</td>");
response.append("<td class='score'>").append(String.format("%.6f", results.get(i).getScore())).append("</td>");
response.append("</tr>");
}
response.append("</tbody></table>");
}
}
response.append("<div class='footer'>نظام بحث موزع يعتمد على خوارزمية TF-IDF</div>");
response.append("</div>");
response.append("</body></html>");
byte[] responseBytes = response.toString().getBytes("UTF-8");
exchange.getResponseHeaders().set("Content-Type", "text/html; charset=UTF-8");
exchange.sendResponseHeaders(200, responseBytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(responseBytes);
}
});
System.out.println("[Frontend] : http://localhost:9000/search");
server.start();
}
}
\ No newline at end of file
package com.search.project;
import com.search.project.proto.SearchRequest;
import com.search.project.proto.SearchResponse;
import com.search.project.proto.SearchServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import org.apache.curator.framework.CuratorFramework;
import java.io.File;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
public class SearchCoordinator {
private static final String DATA_DIR = "src/main/resources";
public static List<SearchResponse.DocumentScore> performDistributedSearch(String query, CuratorFramework client, ServiceRegistry registry) {
List<SearchResponse.DocumentScore> finalResults = new ArrayList<>();
if (query == null || query.isEmpty()) return finalResults;
try {
// 1. جلب كل العمال المسجلين في زوكيبر
List<String> allWorkerNodes = registry.getAllWorkers();
// تحديد العنوان الكامل للقائد الحالي (IP + Port) للاستبعاد
String myIp = InetAddress.getLocalHost().getHostAddress();
String myFullAddress = myIp + ":" + WorkerNode.serverPort;
// قائمة لتخزين عناوين العمال الفعليين الذين سنرسل لهم الطلبات
List<String> validWorkersAddresses = new ArrayList<>();
for (String nodeName : allWorkerNodes) {
byte[] data = client.getData().forPath("/service_registry/" + nodeName);
String address = new String(data);
// الاستبعاد يعتمد على العنوان الكامل وليس الـ IP فقط
if (address.equals(myFullAddress)) {
System.out.println("[Coordinator] أنا القائد: " + address);
continue;
}
validWorkersAddresses.add(address);
}
// 2. تجهيز الملفات
File dir = new File(DATA_DIR);
File[] files = dir.listFiles((d, name) -> name.endsWith(".txt"));
if (files == null || validWorkersAddresses.isEmpty()) {
System.out.println("[Coordinator] لا يوجد ملفات أو لا يوجد عمال متاحون حالياً!");
return finalResults;
}
List<String> allFileNames = new ArrayList<>();
for (File f : files) allFileNames.add(f.getName());
int totalFiles = allFileNames.size();
int numWorkers = validWorkersAddresses.size();
int startIndex = 0;
// 3. توزيع الملفات على العمال المتاحين
List<String> filesRemaining = new ArrayList<>(allFileNames); // قائمة الملفات
int workersLeft = validWorkersAddresses.size(); // عدد العمال
for (String workerAddress : validWorkersAddresses) {
if (filesRemaining.isEmpty()) break;
// حساب حصة العامل الحالي من الملفات "المتبقية" فقط
int chunkSize = (int) Math.ceil((double) filesRemaining.size() / workersLeft);
int toIndex = Math.min(chunkSize, filesRemaining.size());
List<String> workerFiles = new ArrayList<>(filesRemaining.subList(0, toIndex));
String[] parts = workerAddress.split(":");
String host = parts[0];
int port = Integer.parseInt(parts[1]);
System.out.println("[Coordinator] محاولة إرسال " + workerFiles.size() + " ملف إلى العامل: " + workerAddress);
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext()
.build();
try {
SearchServiceGrpc.SearchServiceBlockingStub stub = SearchServiceGrpc.newBlockingStub(channel);
SearchResponse response = stub.processSearch(
SearchRequest.newBuilder()
.setQuery(query)
.addAllFileNames(workerFiles)
.build()
);
finalResults.addAll(response.getResultsList());
// إزالة الملفات التي تمت معالجتها بنجاح من القائمة المتبقية
filesRemaining.removeAll(workerFiles);
System.out.println("[Coordinator] نجح العامل: " + workerAddress);
} catch (Exception e) {
System.err.println("[Coordinator] فشل العامل " + workerAddress + " وسيتم تحويل ملفاته لعامل آخر.");
} finally {
channel.shutdown();
workersLeft--; // تنقيص عدد العمال المتبقيين للمحاولة القادمة
}
}
// 4. حساب الـ IDF والترتيب النهائي
// IDF = log(عدد الملفات الكلي / عدد الملفات التي تحتوي الكلمة)
double idf = Math.log((double) totalFiles / (finalResults.isEmpty() ? 1 : finalResults.size())) ;
List<SearchResponse.DocumentScore> sortedWithIdf = new ArrayList<>();
for (SearchResponse.DocumentScore ds : finalResults) {
sortedWithIdf.add(SearchResponse.DocumentScore.newBuilder()
.setDocName(ds.getDocName())
.setScore(ds.getScore() * idf).build());
}
// ترتيب تنازلي حسب السكور النهائي (TF * IDF)
sortedWithIdf.sort((a, b) -> Double.compare(b.getScore(), a.getScore()));
return sortedWithIdf;
} catch (Exception e) {
e.printStackTrace();
}
return finalResults;
}
}
\ No newline at end of file
...@@ -9,35 +9,48 @@ import java.nio.file.Files; ...@@ -9,35 +9,48 @@ import java.nio.file.Files;
import java.util.Arrays; import java.util.Arrays;
public class SearchServiceImplementation extends SearchServiceGrpc.SearchServiceImplBase { public class SearchServiceImplementation extends SearchServiceGrpc.SearchServiceImplBase {
private static final String DATA_DIR = "D:/search_data"; // تأكدي من وجود المجلد وبعض ملفات .txt private static final String DATA_DIR = "src/main/resources";
@Override @Override
public void processSearch(SearchRequest request, StreamObserver<SearchResponse> responseObserver) { public void processSearch(SearchRequest request, StreamObserver<SearchResponse> responseObserver) {
String query = request.getQuery().toLowerCase(); // 1. تقطيع جملة البحث لعدة كلمات
StringBuilder results = new StringBuilder(); String[] queryWords = request.getQuery().toLowerCase().split("\\W+");
SearchResponse.Builder responseBuilder = SearchResponse.newBuilder();
File dir = new File(DATA_DIR);
File[] files = dir.listFiles((d, name) -> name.endsWith(".txt")); for (String fileName : request.getFileNamesList()) {
try {
if (files != null) { File file = new File(DATA_DIR, fileName);
for (File file : files) { if (!file.exists()) continue;
try {
String content = Files.readString(file.toPath()).toLowerCase(); String content = Files.readString(file.toPath()).toLowerCase();
String[] words = content.split("\\W+"); String[] docWords = content.split("\\W+");
long count = Arrays.stream(words).filter(w -> w.equals(query)).count();
double tf = (words.length > 0) ? (double) count / words.length : 0; double totalSentenceScore = 0;
if (tf > 0) { // 2. نحسب الـ TF لكل كلمة من كلمات البحث ونجمعهم
results.append(String.format("File: %-12s | Score: %.4f\n", file.getName(), tf)); for (String qWord : queryWords) {
} if (qWord.isEmpty()) continue;
} catch (Exception e) {
results.append("Error reading: ").append(file.getName()).append("\n"); long count = Arrays.stream(docWords).filter(w -> w.equals(qWord)).count();
double tf = (docWords.length > 0) ? (double) count / docWords.length : 0;
totalSentenceScore += tf; // نجمع الأوزان
}
if (totalSentenceScore > 0) {
SearchResponse.DocumentScore docScore = SearchResponse.DocumentScore.newBuilder()
.setDocName(fileName)
.setScore(totalSentenceScore)
.build();
responseBuilder.addResults(docScore);
} }
} catch (Exception e) {
System.err.println("Error reading: " + fileName);
} }
} }
String finalMsg = results.length() > 0 ? results.toString() : "No results found."; responseObserver.onNext(responseBuilder.build());
responseObserver.onNext(SearchResponse.newBuilder().setResult(finalMsg).build());
responseObserver.onCompleted(); responseObserver.onCompleted();
} }
} }
\ No newline at end of file
...@@ -8,7 +8,7 @@ import java.net.InetAddress; ...@@ -8,7 +8,7 @@ import java.net.InetAddress;
import java.util.List; import java.util.List;
public class ServiceRegistry { public class ServiceRegistry {
private static final String ELECTION_PATH = "/election"; private static final String ELECTION_PATH = "/election_test_1";
private static final String REGISTRY_PATH = "/service_registry"; private static final String REGISTRY_PATH = "/service_registry";
private final CuratorFramework client; private final CuratorFramework client;
...@@ -29,7 +29,10 @@ public class ServiceRegistry { ...@@ -29,7 +29,10 @@ public class ServiceRegistry {
} }
}); });
leaderSelector.autoRequeue(); leaderSelector.autoRequeue();
System.out.println("جاري الدخول في الانتخابات...");
leaderSelector.start(); leaderSelector.start();
System.out.println("بدأت عملية الانتخاب، بانتظار النتائج...");
} }
public void registerToCluster(String metadata) throws Exception { public void registerToCluster(String metadata) throws Exception {
......
package com.search.project;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import java.net.InetAddress;
import java.net.ServerSocket;
public class WorkerNode {
// لحفظ المنفذ الحالي للعقدة
public static int serverPort = -1;
public static void start(ServiceRegistry registry) throws Exception {
int port;
// حجز منفذ ديناميكي
try (ServerSocket socket = new ServerSocket(0)) {
port = socket.getLocalPort();
}
// حفظ المنفذ في المتغير العام لاستخدامه في الفلترة عند المنسق
serverPort = port;
String myAddress = InetAddress.getLocalHost().getHostAddress() + ":" + port;
// تسجيل العقدة في زوكيبر
registry.registerToCluster(myAddress);
// بناء وتشغيل سيرفر gRPC
Server server = ServerBuilder.forPort(port)
.addService(new SearchServiceImplementation())
.build();
System.out.println("[Worker] العامل جاهز على: " + myAddress);
server.start();
}
}
\ No newline at end of file
syntax = "proto3"; syntax = "proto3";
// هذا السطر مهم جداً ليتطابق مع بنية مجلداتك
option java_package = "com.search.project.proto"; option java_package = "com.search.project.proto";
option java_multiple_files = true; option java_multiple_files = true;
...@@ -12,8 +13,12 @@ service SearchService { ...@@ -12,8 +13,12 @@ service SearchService {
message SearchRequest { message SearchRequest {
string query = 1; string query = 1;
repeated string file_names = 2;
} }
message SearchResponse { message SearchResponse {
string result = 1; message DocumentScore {
string doc_name = 1;
double score = 2;
}
repeated DocumentScore results = 1;
} }
\ No newline at end of file
The future of transportation is moving toward autonomous or self-driving technology. These systems use a combination of cameras, LiDAR, and radar sensors to perceive the environment around the car. Artificial intelligence algorithms process this data in real-time to make decisions about steering, braking, and navigation. Companies like Waymo and Tesla are at the forefront of developing software that can handle complex urban traffic without human intervention. There are different levels of autonomy, ranging from simple cruise control to fully driverless systems where no steering wheel is needed. While autonomous cars promise to reduce accidents caused by human error, there are still many legal and ethical questions to be answered. Ensuring the security of these connected systems is critical to prevent hackers from taking control of the vehicle remotely.
\ No newline at end of file
Regular car maintenance is essential for ensuring the safety and longevity of any vehicle. One of the most important tasks is changing the engine oil and filters to keep the moving parts lubricated and clean. Tires should also be checked frequently for proper pressure and tread depth to prevent blowouts and ensure good braking performance. Brakes are a critical safety system, and any unusual noises or vibrations during braking should be inspected immediately by a professional. Checking fluid levels, such as coolant and brake fluid, can prevent the engine from overheating or the brakes from failing. Modern cars also have various safety features like airbags, seatbelts, and anti-lock braking systems (ABS) that require periodic checks. Keeping a detailed maintenance log can help maintain the resale value of the car and provide peace of mind for the owner.
\ No newline at end of file
Classic car restoration is a passionate hobby for many automotive enthusiasts who appreciate the history and design of older vehicles. A car is typically considered a classic if it is more than twenty or thirty years old and has been well-maintained. The process of restoration involves repairing the engine, bodywork, and interior to bring the vehicle back to its original factory condition. Finding authentic spare parts for vintage models like the Ford Mustang or the Jaguar E-Type can be a difficult and expensive task. Collectors often spend years working on a single project, attending car shows to display their hard work. Unlike modern cars, classic vehicles lack advanced electronics, offering a more raw and mechanical driving experience. The value of classic cars can increase significantly over time, making them a unique form of investment for some.
\ No newline at end of file
Electric vehicles are transforming the global automotive landscape as countries aim to reduce carbon emissions. These cars rely on large lithium-ion battery packs to power electric motors, eliminating the need for internal combustion engines. Tesla has been a leader in this industry, popularizing long-range electric travel and high-tech software integration. Charging infrastructure is expanding rapidly, with superchargers becoming more common in cities and along highways. The main advantage of electric cars is their efficiency and lower maintenance costs compared to traditional gasoline vehicles. However, challenges like battery recycling and the environmental impact of mining raw materials remain significant concerns. Many manufacturers like Ford and Volkswagen are now shifting their entire production lines toward electric models to stay competitive in the changing market.
\ No newline at end of file
Formula 1 is the pinnacle of automotive engineering and international motor racing. The cars used in F1 are the fastest regulated road-course racing cars in the world, capable of reaching incredible speeds. Engineering teams spend hundreds of millions of dollars every year to gain a fraction of a second in performance. Aerodynamics plays a massive role, with front and rear wings designed to push the car down onto the track for better grip. F1 engines are highly advanced hybrid power units that produce over a thousand horsepower from a small 1.6-liter V6 engine. Safety has also improved drastically over the years, with the introduction of the halo device to protect the driver's head. Every race is a test of both human skill and technical innovation, as drivers push their machines to the absolute limit under extreme physical pressure.
\ No newline at end of file
Hybrid cars combine a traditional gasoline engine with an electric motor to improve fuel efficiency and reduce emissions. There are different types of hybrids, including mild hybrids, full hybrids, and plug-in hybrids (PHEVs). Full hybrids like the Toyota Prius use the electric motor for low-speed driving and the gasoline engine for higher speeds or acceleration. Plug-in hybrids have larger batteries that can be charged from an external outlet, allowing for several miles of purely electric driving. The system automatically switches between power sources to optimize performance based on driving conditions. Regenerative braking is a key feature of hybrid technology, where the energy lost during braking is captured and stored in the battery. This makes hybrid vehicles especially efficient in stop-and-go city traffic, where traditional cars waste a lot of fuel.
\ No newline at end of file
Hydrogen fuel cell vehicles (FCVs) represent another promising alternative to traditional gasoline engines. These cars use a fuel cell stack to convert hydrogen gas and oxygen from the air into electricity, which then powers an electric motor. The only byproduct of this process is pure water vapor, making hydrogen cars completely zero-emission. One of the main advantages of hydrogen over battery-electric cars is the fast refueling time, which takes only a few minutes. Brands like Toyota and Hyundai have already released hydrogen models like the Mirai and Nexo. However, the lack of a widespread hydrogen refueling network and the high cost of producing green hydrogen are major barriers to adoption. Despite these challenges, hydrogen technology is seen as a great solution for heavy-duty transportation, such as trucks and buses, where large batteries would be too heavy.
\ No newline at end of file
Luxury cars are designed to provide the highest levels of comfort, status, and advanced technology. Brands like Mercedes-Benz, BMW, and Rolls-Royce focus on using premium materials such as fine leather, real wood trim, and high-quality soundproofing. These vehicles often feature adaptive air suspension systems that smooth out bumps in the road, providing a magic carpet ride experience. Beyond physical comfort, luxury cars are equipped with the latest infotainment systems, including multiple screens and voice-activated assistants. Safety is also a priority, with advanced driver assistance systems that can take over steering or braking in emergencies. Owning a luxury vehicle is often seen as a symbol of success, but it also comes with high insurance and maintenance costs due to the complexity of the specialized parts.
\ No newline at end of file
Off-road vehicles are built to tackle the toughest terrains, from rocky mountains to deep mud and sandy deserts. These cars, such as the Jeep Wrangler and the Toyota Land Cruiser, feature high ground clearance and heavy-duty suspension systems. Four-wheel drive (4WD) is an essential feature, allowing power to be sent to all wheels for better traction on slippery surfaces. Specialized tires with deep treads provide the necessary grip to climb steep hills or navigate through water crossings. Protection is also important, with many off-roaders installing skid plates to protect the underbody from rocks and damage. For many people, off-roading is a lifestyle that involves camping and exploring remote areas where paved roads do not exist. Reliability is the most important factor for an off-road vehicle, as a breakdown in the wilderness can be dangerous.
\ No newline at end of file
Sports cars are all about speed, agility, and the thrill of driving. Engineers prioritize aerodynamics and weight distribution to ensure these cars can handle sharp corners at high speeds. Engines in sports cars are often turbocharged or supercharged to deliver maximum horsepower and torque. Lightweight materials like carbon fiber and aluminum are used to keep the vehicle's weight low, improving the power-to-weight ratio. Famous models like the Porsche 911 and the Ferrari F8 are legendary for their track performance and iconic engine sounds. While these cars are exciting to drive, they often have limited cargo space and a firm ride that might not be suitable for daily commuting. Most sports cars also feature manual or dual-clutch transmissions to give the driver more control over gear shifts and acceleration.
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment